summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--debian/README.Debian16
-rw-r--r--debian/README.source18
-rw-r--r--debian/argyll-dbg.postinst40
-rw-r--r--debian/argyll-dbg.postrm33
-rw-r--r--debian/argyll-dbg.preinst31
-rw-r--r--debian/argyll-doc.doc-base7
-rw-r--r--debian/argyll-doc.docs2
-rw-r--r--debian/argyll-ref.install1
-rw-r--r--debian/argyll-ref.postinst40
-rw-r--r--debian/argyll-ref.postrm33
-rw-r--r--debian/argyll-ref.preinst31
-rw-r--r--debian/argyll.install3
-rw-r--r--debian/argyll.manpages1
-rw-r--r--debian/argyll.postinst40
-rw-r--r--debian/argyll.postrm34
-rw-r--r--debian/argyll.preinst32
-rw-r--r--debian/changelog811
-rw-r--r--debian/compat1
-rw-r--r--debian/control65
-rw-r--r--debian/copyright977
-rw-r--r--debian/icc-utils.postinst41
-rw-r--r--debian/icc-utils.postrm33
-rw-r--r--debian/icc-utils.preinst32
-rw-r--r--debian/man/applycal.130
-rw-r--r--debian/man/average.129
-rw-r--r--debian/man/cb2ti3.121
-rw-r--r--debian/man/cctiff.173
-rw-r--r--debian/man/ccxxmake.191
-rw-r--r--debian/man/chartread.196
-rw-r--r--debian/man/collink.1241
-rw-r--r--debian/man/colprof.1186
-rw-r--r--debian/man/colverify.168
-rw-r--r--debian/man/dispcal.1156
-rw-r--r--debian/man/dispread.1103
-rw-r--r--debian/man/dispwin.181
-rw-r--r--debian/man/extracticc.112
-rw-r--r--debian/man/extractttag.118
-rw-r--r--debian/man/fakeCMY.121
-rw-r--r--debian/man/fakeread.190
-rw-r--r--debian/man/greytiff.118
-rw-r--r--debian/man/iccdump.121
-rw-r--r--debian/man/iccgamut.197
-rw-r--r--debian/man/icclu.134
-rw-r--r--debian/man/illumread.141
-rw-r--r--debian/man/invprofcheck.145
-rw-r--r--debian/man/kodak2ti3.124
-rw-r--r--debian/man/ls2ti3.113
-rw-r--r--debian/man/mppcheck.129
-rw-r--r--debian/man/mpplu.157
-rw-r--r--debian/man/mppprof.130
-rw-r--r--debian/man/oeminst.128
-rw-r--r--debian/man/printcal.177
-rw-r--r--debian/man/printtarg.1125
-rw-r--r--debian/man/profcheck.161
-rw-r--r--debian/man/refine.157
-rw-r--r--debian/man/revfix.149
-rw-r--r--debian/man/scanin.1114
-rw-r--r--debian/man/spec2cie.147
-rw-r--r--debian/man/specplot.128
-rw-r--r--debian/man/splitti3.133
-rw-r--r--debian/man/spotread.1123
-rw-r--r--debian/man/synthcal.147
-rw-r--r--debian/man/synthread.145
-rw-r--r--debian/man/targen.1112
-rw-r--r--debian/man/tiffgamut.190
-rw-r--r--debian/man/timage.133
-rw-r--r--debian/man/txt2ti3.133
-rw-r--r--debian/man/viewgam.142
-rw-r--r--debian/man/xicclu.1162
-rw-r--r--debian/missing-sources/deep_arrays.json18
-rw-r--r--debian/missing-sources/difficult_json_c_test_case.json7
-rw-r--r--debian/missing-sources/difficult_json_c_test_case_with_comments.json7
-rw-r--r--debian/missing-sources/non_utf8_char_in_string.json88
-rw-r--r--debian/missing-sources/x3dom.js53578
-rw-r--r--debian/patches/0001_jam.patch143
-rw-r--r--debian/patches/0100_spelling.patch1720
-rw-r--r--debian/patches/0105_dispwin_segfault.patch20
-rw-r--r--debian/patches/0110_usb-db_new.patch19
-rw-r--r--debian/patches/0115_hurd_PATH_MAX.patch81
-rw-r--r--debian/patches/0120_kfreebsd.patch55
-rw-r--r--debian/patches/0125_gcc5.patch22
-rw-r--r--debian/patches/0130_openssl.patch19
-rw-r--r--debian/patches/series8
-rwxr-xr-xdebian/repack.sh56
-rwxr-xr-xdebian/rules105
-rw-r--r--debian/source/format1
-rw-r--r--debian/tools/buildman.sh13
-rw-r--r--debian/watch5
88 files changed, 61318 insertions, 0 deletions
diff --git a/debian/README.Debian b/debian/README.Debian
new file mode 100644
index 0000000..b707ba4
--- /dev/null
+++ b/debian/README.Debian
@@ -0,0 +1,16 @@
+argyll for Debian
+-----------------
+
+Notes for KFreeBSD-*:
+
+From spectro/usbio_bsd.c:
+
+ !!!! This driver is incomplete and non-functional !!!!
+ BSD uses fd per end point, so simplifies things.
+ No clear ep or abort i/o though, so we could try clear halt,
+ or close fd and see if that works in aborting transaction ?
+ Posix aio would probably work, but it's not loaded by default :-(
+ Could use libusb20 API, but not backwards or cross compatible,
+ and is very likely to be buggy ?
+
+ -- Jörg Frings-Fürst <debian@jff-webhosting.net> Mon, 29 Sep 2014 09:17:15 +0200
diff --git a/debian/README.source b/debian/README.source
new file mode 100644
index 0000000..e4f2b3d
--- /dev/null
+++ b/debian/README.source
@@ -0,0 +1,18 @@
+Hello,
+
+now I use the branching model from Vincent Driessen[1].
+
+I use the gitflow-avh[2]. with the Documentation[3].
+The Debian package can be found here[4].
+
+Please upload unattended uploads use a branch feature/<your title>.
+
+
+Many thanks.
+
+ -- Jörg Frings-Fürst <debian@jff-webhosting.net> Fri, 02 Jun 2017 19:00:40 +0200
+
+[1] http://nvie.com/posts/a-successful-git-branching-model/
+[2] https://github.com/petervanderdoes/gitflow-avh
+[3] https://github.com/petervanderdoes/gitflow-avh/wiki
+[4] https://tracker.debian.org/pkg/git-flow
diff --git a/debian/argyll-dbg.postinst b/debian/argyll-dbg.postinst
new file mode 100644
index 0000000..66ce5b3
--- /dev/null
+++ b/debian/argyll-dbg.postinst
@@ -0,0 +1,40 @@
+#!/bin/sh
+#
+# see: dh_installdeb(1)
+
+set -e
+
+# summary of how this script can be called:
+# * <postinst> `configure' <most-recently-configured-version>
+# * <old-postinst> `abort-upgrade' <new version>
+# * <conflictor's-postinst> `abort-remove' `in-favour' <package>
+# <new-version>
+# * <postinst> `abort-remove'
+# * <deconfigured's-postinst> `abort-deconfigure' `in-favour'
+# <failed-install-package> <version> `removing'
+# <conflicting-package> <version>
+# for details, see http://www.debian.org/doc/debian-policy/ or
+# the debian-policy package
+
+# source debconf library
+#. /usr/share/debconf/confmodule
+
+
+case "$1" in
+
+ configure|abort-upgrade|abort-remove|abort-deconfigure)
+ # Replace documentation directory symlink
+ dpkg-maintscript-helper symlink_to_dir /usr/share/doc/argyll-dbg /usr/share/doc/argyll-doc 1.6.3-2~ -- "$@"
+ ;;
+
+ *)
+ echo "postinst called with unknown argument \`$1'" >&2
+ exit 1
+ ;;
+
+esac
+
+#DEBHELPER#
+
+
+exit 0
diff --git a/debian/argyll-dbg.postrm b/debian/argyll-dbg.postrm
new file mode 100644
index 0000000..0ebb9d3
--- /dev/null
+++ b/debian/argyll-dbg.postrm
@@ -0,0 +1,33 @@
+#! /bin/sh
+# postrm script for argyll
+#
+# see: dh_installdeb(1)
+
+set -e
+
+# summary of how this script can be called:
+# * <postrm> `remove'
+# * <postrm> `purge'
+# * <old-postrm> `upgrade' <new-version>
+# * <new-postrm> `failed-upgrade' <old-version>
+# * <new-postrm> `abort-install'
+# * <new-postrm> `abort-install' <old-version>
+# * <new-postrm> `abort-upgrade' <old-version>
+# * <disappearer's-postrm> `disappear' <r>overwrit>r> <new-version>
+# for details, see /usr/share/doc/packaging-manual/
+
+case "$1" in
+ purge|remove|upgrade|failed-upgrade|abort-install|abort-upgrade|disappear)
+ dpkg-maintscript-helper symlink_to_dir /usr/share/doc/argyll-dbg /usr/share/doc/argyll-doc 1.6.3-2~ -- "$@"
+ ;;
+
+ *)
+ echo "postrm called with unknown argument \`$1'" >&2
+ exit 0
+
+esac
+
+# dh_installdeb will replace this with shell code automatically
+# generated by other debhelper scripts.
+
+#DEBHELPER#
diff --git a/debian/argyll-dbg.preinst b/debian/argyll-dbg.preinst
new file mode 100644
index 0000000..94caee7
--- /dev/null
+++ b/debian/argyll-dbg.preinst
@@ -0,0 +1,31 @@
+#!/bin/sh
+# preinst script for #PACKAGE#
+#
+# see: dh_installdeb(1)
+
+set -e
+
+# summary of how this script can be called:
+# * <new-preinst> `install'
+# * <new-preinst> `install' <old-version>
+# * <new-preinst> `upgrade' <old-version>
+# * <old-preinst> `abort-upgrade' <new-version>
+# for details, see http://www.debian.org/doc/debian-policy/ or
+# the debian-policy package
+
+case "$1" in
+ install|upgrade|abort-upgrade)
+ dpkg-maintscript-helper symlink_to_dir /usr/share/doc/argyll-dbg /usr/share/doc/argyll-doc 1.6.3-2~ -- "$@"
+ ;;
+ *)
+ echo "preinst called with unknown argument \`$1'" >&2
+ exit 1
+ ;;
+esac
+
+# dh_installdeb will replace this with shell code automatically
+# generated by other debhelper scripts.
+
+#DEBHELPER#
+
+exit 0
diff --git a/debian/argyll-doc.doc-base b/debian/argyll-doc.doc-base
new file mode 100644
index 0000000..962b063
--- /dev/null
+++ b/debian/argyll-doc.doc-base
@@ -0,0 +1,7 @@
+Document: argyll
+Title: Argyll Documentation
+Section: Graphics
+
+Format: HTML
+Index: /usr/share/doc/argyll-doc/ArgyllDoc.html
+Files: /usr/share/doc/argyll-doc/*.html
diff --git a/debian/argyll-doc.docs b/debian/argyll-doc.docs
new file mode 100644
index 0000000..f7d7b3b
--- /dev/null
+++ b/debian/argyll-doc.docs
@@ -0,0 +1,2 @@
+doc/*
+Readme.txt
diff --git a/debian/argyll-ref.install b/debian/argyll-ref.install
new file mode 100644
index 0000000..beefdac
--- /dev/null
+++ b/debian/argyll-ref.install
@@ -0,0 +1 @@
+usr/share/color/argyll/ref
diff --git a/debian/argyll-ref.postinst b/debian/argyll-ref.postinst
new file mode 100644
index 0000000..5584c80
--- /dev/null
+++ b/debian/argyll-ref.postinst
@@ -0,0 +1,40 @@
+#!/bin/sh
+#
+# see: dh_installdeb(1)
+
+set -e
+
+# summary of how this script can be called:
+# * <postinst> `configure' <most-recently-configured-version>
+# * <old-postinst> `abort-upgrade' <new version>
+# * <conflictor's-postinst> `abort-remove' `in-favour' <package>
+# <new-version>
+# * <postinst> `abort-remove'
+# * <deconfigured's-postinst> `abort-deconfigure' `in-favour'
+# <failed-install-package> <version> `removing'
+# <conflicting-package> <version>
+# for details, see http://www.debian.org/doc/debian-policy/ or
+# the debian-policy package
+
+# source debconf library
+#. /usr/share/debconf/confmodule
+
+
+case "$1" in
+
+ configure|abort-upgrade|abort-remove|abort-deconfigure)
+ # Replace documentation directory symlink
+ dpkg-maintscript-helper symlink_to_dir /usr/share/doc/argyll-ref /usr/share/doc/argyll-doc 1.6.3-2~ -- "$@"
+ ;;
+
+ *)
+ echo "postinst called with unknown argument \`$1'" >&2
+ exit 1
+ ;;
+
+esac
+
+#DEBHELPER#
+
+
+exit 0
diff --git a/debian/argyll-ref.postrm b/debian/argyll-ref.postrm
new file mode 100644
index 0000000..3741e51
--- /dev/null
+++ b/debian/argyll-ref.postrm
@@ -0,0 +1,33 @@
+#! /bin/sh
+# postrm script for argyll
+#
+# see: dh_installdeb(1)
+
+set -e
+
+# summary of how this script can be called:
+# * <postrm> `remove'
+# * <postrm> `purge'
+# * <old-postrm> `upgrade' <new-version>
+# * <new-postrm> `failed-upgrade' <old-version>
+# * <new-postrm> `abort-install'
+# * <new-postrm> `abort-install' <old-version>
+# * <new-postrm> `abort-upgrade' <old-version>
+# * <disappearer's-postrm> `disappear' <r>overwrit>r> <new-version>
+# for details, see /usr/share/doc/packaging-manual/
+
+case "$1" in
+ purge|remove|upgrade|failed-upgrade|abort-install|abort-upgrade|disappear)
+ dpkg-maintscript-helper symlink_to_dir /usr/share/doc/argyll-ref /usr/share/doc/argyll-doc 1.6.3-2~ -- "$@"
+ ;;
+
+ *)
+ echo "postrm called with unknown argument \`$1'" >&2
+ exit 0
+
+esac
+
+# dh_installdeb will replace this with shell code automatically
+# generated by other debhelper scripts.
+
+#DEBHELPER#
diff --git a/debian/argyll-ref.preinst b/debian/argyll-ref.preinst
new file mode 100644
index 0000000..d5fdfc6
--- /dev/null
+++ b/debian/argyll-ref.preinst
@@ -0,0 +1,31 @@
+#!/bin/sh
+# preinst script for #PACKAGE#
+#
+# see: dh_installdeb(1)
+
+set -e
+
+# summary of how this script can be called:
+# * <new-preinst> `install'
+# * <new-preinst> `install' <old-version>
+# * <new-preinst> `upgrade' <old-version>
+# * <old-preinst> `abort-upgrade' <new-version>
+# for details, see http://www.debian.org/doc/debian-policy/ or
+# the debian-policy package
+
+case "$1" in
+ install|upgrade|abort-upgrade)
+ dpkg-maintscript-helper symlink_to_dir /usr/share/doc/argyll-ref /usr/share/doc/argyll-doc 1.6.3-2~ -- "$@"
+ ;;
+ *)
+ echo "preinst called with unknown argument \`$1'" >&2
+ exit 1
+ ;;
+esac
+
+# dh_installdeb will replace this with shell code automatically
+# generated by other debhelper scripts.
+
+#DEBHELPER#
+
+exit 0
diff --git a/debian/argyll.install b/debian/argyll.install
new file mode 100644
index 0000000..0b1afee
--- /dev/null
+++ b/debian/argyll.install
@@ -0,0 +1,3 @@
+usb/55-Argyll.rules lib/udev/rules.d/
+usb/Argyll.usermap etc/hotplug/usb
+usr/bin/*
diff --git a/debian/argyll.manpages b/debian/argyll.manpages
new file mode 100644
index 0000000..13cdaf4
--- /dev/null
+++ b/debian/argyll.manpages
@@ -0,0 +1 @@
+debian/man/*.1
diff --git a/debian/argyll.postinst b/debian/argyll.postinst
new file mode 100644
index 0000000..7621237
--- /dev/null
+++ b/debian/argyll.postinst
@@ -0,0 +1,40 @@
+#!/bin/sh
+#
+# see: dh_installdeb(1)
+
+set -e
+
+# summary of how this script can be called:
+# * <postinst> `configure' <most-recently-configured-version>
+# * <old-postinst> `abort-upgrade' <new version>
+# * <conflictor's-postinst> `abort-remove' `in-favour' <package>
+# <new-version>
+# * <postinst> `abort-remove'
+# * <deconfigured's-postinst> `abort-deconfigure' `in-favour'
+# <failed-install-package> <version> `removing'
+# <conflicting-package> <version>
+# for details, see http://www.debian.org/doc/debian-policy/ or
+# the debian-policy package
+
+# source debconf library
+#. /usr/share/debconf/confmodule
+
+
+case "$1" in
+
+ configure|abort-upgrade|abort-remove|abort-deconfigure)
+ # Replace documentation directory symlink
+ dpkg-maintscript-helper symlink_to_dir /usr/share/doc/argyll /usr/share/doc/argyll-doc 1.6.3-2~ -- "$@"
+ ;;
+
+ *)
+ echo "postinst called with unknown argument \`$1'" >&2
+ exit 1
+ ;;
+
+esac
+
+#DEBHELPER#
+
+
+exit 0
diff --git a/debian/argyll.postrm b/debian/argyll.postrm
new file mode 100644
index 0000000..f9614cb
--- /dev/null
+++ b/debian/argyll.postrm
@@ -0,0 +1,34 @@
+#! /bin/sh
+# postrm script for argyll
+#
+# see: dh_installdeb(1)
+
+set -e
+
+# summary of how this script can be called:
+# * <postrm> `remove'
+# * <postrm> `purge'
+# * <old-postrm> `upgrade' <new-version>
+# * <new-postrm> `failed-upgrade' <old-version>
+# * <new-postrm> `abort-install'
+# * <new-postrm> `abort-install' <old-version>
+# * <new-postrm> `abort-upgrade' <old-version>
+# * <disappearer's-postrm> `disappear' <r>overwrit>r> <new-version>
+# for details, see /usr/share/doc/packaging-manual/
+
+case "$1" in
+ purge|remove|upgrade|failed-upgrade|abort-install|abort-upgrade|disappear)
+ rm -rf /var/lib/argyll
+ dpkg-maintscript-helper symlink_to_dir /usr/share/doc/argyll /usr/share/doc/argyll-doc 1.6.3-2~ -- "$@"
+ ;;
+
+ *)
+ echo "postrm called with unknown argument \`$1'" >&2
+ exit 0
+
+esac
+
+# dh_installdeb will replace this with shell code automatically
+# generated by other debhelper scripts.
+
+#DEBHELPER#
diff --git a/debian/argyll.preinst b/debian/argyll.preinst
new file mode 100644
index 0000000..a3bdca6
--- /dev/null
+++ b/debian/argyll.preinst
@@ -0,0 +1,32 @@
+#!/bin/sh
+# preinst script for #PACKAGE#
+#
+# see: dh_installdeb(1)
+
+set -e
+
+# summary of how this script can be called:
+# * <new-preinst> `install'
+# * <new-preinst> `install' <old-version>
+# * <new-preinst> `upgrade' <old-version>
+# * <old-preinst> `abort-upgrade' <new-version>
+# for details, see http://www.debian.org/doc/debian-policy/ or
+# the debian-policy package
+
+
+case "$1" in
+ install|upgrade|abort-upgrade)
+ dpkg-maintscript-helper symlink_to_dir /usr/share/doc/argyll /usr/share/doc/argyll-doc 1.6.3-2~ -- "$@"
+ ;;
+ *)
+ echo "preinst called with unknown argument \`$1'" >&2
+ exit 1
+ ;;
+esac
+
+# dh_installdeb will replace this with shell code automatically
+# generated by other debhelper scripts.
+
+#DEBHELPER#
+
+exit 0
diff --git a/debian/changelog b/debian/changelog
new file mode 100644
index 0000000..bb38595
--- /dev/null
+++ b/debian/changelog
@@ -0,0 +1,811 @@
+argyll (2.0.0+repack-2) UNRELEASED; urgency=medium
+
+ * debian/copyright: Add copyright for numlib/quadprog.*.
+
+ -- Jörg Frings-Fürst <debian@jff.email> Sun, 03 Dec 2017 20:41:38 +0100
+
+argyll (2.0.0+repack-1) unstable; urgency=medium
+
+ * Switch from repack to Files-Excluded.
+ * Refresh patches.
+ * Change to my new email address.
+ * debian/changelog:
+ - Remove trailing whitespaces.
+ * debian/control:
+ - Remove duplicates Priority fields.
+ - Requested versions are lesser as there are in old-stable:
+ + Remove dpkg amd dpkg-dev from (Build|Pre)-Depends.
+ + Remove versions from Recommend udev.
+ + Remove Replaces & Breaks argyll from argyll-ref.
+ * debian/rules:
+ - Add script to remove executable flag from upstream source.
+ * Declare compliance with Debian Policy 4.1.2.0 (No changes needed).
+ * Change to secure URI.
+ - Homepage in debian/copyright, debian/watch and debian/control.
+ * debian/patches/0130_openssl.patch:
+ - Switch from SSLv23_client_method to TLS_client_method (Closes: #871427).
+
+ -- Jörg Frings-Fürst <debian@jff.email> Fri, 01 Dec 2017 10:35:30 +0100
+
+argyll (1.9.2+repack-2) unstable; urgency=medium
+
+ * New README.source to explain the branching model used.
+ * Declare compliance with Debian Policy 4.0.1. (No changes needed).
+ * debian/copyright:
+ - Add 2017 to debian/*.
+ * debian/control:
+ - Remove dh-autoreconf.
+ + Not longer needed since debhelper compatibility level >= 10.
+ * debian/rules:
+ - For better handling replace dpkg-parsechangelog with SOURCE_DATE_EPOCH
+ from pkg-info.mk.
+ * New patch 0130_openssl.patch:
+ - Enable all ssl connections since openssl provides only connetions with
+ TLS1.2 or greater (Closes: #871427).
+ * Use the automatic debug symbol packages:
+ - Remove argyll-dbg section from debian/control.
+ - Use --dbgsym-migration at override_dh_strip from debian/rules.
+ * Refresh patches:
+ - 0100_spelling.patch.
+
+ -- Jörg Frings-Fürst <debian@jff-webhosting.net> Thu, 31 Aug 2017 06:49:06 +0200
+
+argyll (1.9.2+repack-1) unstable; urgency=medium
+
+ * New upstream release.
+ - Refresh patches.
+ * Bump compat to 10:
+ - Change debian/compat to 10.
+ - Change debhelper version at debian/control to >= 10.
+ * debian/control:
+ - Bump Standards-Version to 3.9.8 (no changes required).
+ - Change VCS-Browser to secure URI.
+ * debian/copyright:
+ - Add year 2016 for upstream.
+ * debian/rules:
+ - Make the build reproducible (Closes: #835265).
+ (Thanks to Chris Lamb <lamby@debian.org>)
+ - Remove License4.txt from argyll-doc.
+ * debian/watch:
+ - Bump Version to 4 (no changes required).
+ * Enable hardening:
+ - Refresh debian/patches/0001_jam.patch.
+ - Add hardening=+all to DEB_BUILD_MAINT_OPTIONS at debian/rules.
+
+ -- Jörg Frings-Fürst <debian@jff-webhosting.net> Sun, 23 Oct 2016 03:37:00 +0200
+
+argyll (1.8.3+repack-2) unstable; urgency=medium
+
+ * debian/copyright: Add 2016.
+ * debian/control:
+ - Transition libpng: Change Build-Depends from libpng16-dev to libpng-dev
+ (Closes: #810178).
+ - Transition libusb: Change Build-Depends from libusb-dev to
+ libusb-1.0-0-dev (Closes: #810398).
+ - Set priority of argyll-ref to extra.
+ * Rename patches.
+ * Refresh debian/patches/0100_spelling.patch.
+
+ -- Jörg Frings-Fürst <debian@jff-webhosting.net> Sun, 17 Jan 2016 18:59:21 +0100
+
+argyll (1.8.3+repack-1) unstable; urgency=medium
+
+ * New upstream release.
+ * Refresh patches.
+ * Move download and repack from debian/rules to uscan:
+ - Add debian debian/repack.sh to debian/watch.
+ - New debian/repack.sh.
+ - Remove get-orig-source from debian/rules.
+ * debian/rules:
+ - Remove tests for the gcc-5 transition.
+ * Rename debian/missing-sources/x3dom.debug.js to x3dom.js.
+
+ -- Jörg Frings-Fürst <debian@jff-webhosting.net> Fri, 06 Nov 2015 09:03:08 +0100
+
+argyll (1.8.2+repack-1) unstable; urgency=medium
+
+ * New upstream release.
+ * Add x3dom.debug.js to debian/missing-sources.
+ * debian/control:
+ - For use of the system library instead of the Argyll library
+ add libssl-dev to Build-Depends
+ * debian/patches/100_spelling.patch:
+ - Add some more typos.
+
+ -- Jörg Frings-Fürst <debian@jff-webhosting.net> Wed, 30 Sep 2015 16:03:54 +0200
+
+argyll (1.8.0+repack-1) unstable; urgency=low
+
+ * New upstream release.
+ * Refresh patches:
+ - debian/patches/15_jam.patch
+ - debian/patches/100_spelling.patch
+ - debian/patches/110_dispwin_segfault.patch
+ * debian/rules:
+ - Rewrite VERSION sniplet.
+ - get-orig-source:
+ + Remove executable flag from source.
+ * Refresh debian/copyright.
+
+ -- Jörg Frings-Fürst <debian@jff-webhosting.net> Sun, 23 Aug 2015 17:27:26 +0200
+
+argyll (1.7.0+repack-4) unstable; urgency=medium
+
+ * debian/rules:
+ - Add --utc to the command date for CRDATE to make the results
+ reproducible over different timezones.
+ * New debian/patches/30_gcc5.patch:
+ - Add gcc-5 support (Closes: #777779).
+ + Thanks to James Cowgill <james410@cowgill.org.uk>.
+ * Re-enable and refresh debian/patches/20_hurd_PATH_MAX.patch.
+
+ -- Jörg Frings-Fürst <debian@jff-webhosting.net> Sun, 05 Jul 2015 12:30:03 +0200
+
+argyll (1.7.0+repack-3) unstable; urgency=medium
+
+ * debian/watch:
+ - change opts to support for "+repack".
+ * debian/patches/15_jam.patch:
+ - Add support for mips64el (Closes: #788447).
+ * debian/control:
+ - Correct some priorities.
+ - Remove package icc-utils because last version
+ in stable is greater then 1.6.3-1.
+ * Remove useless debian/argyll.lintian-overrides.
+
+ -- Jörg Frings-Fürst <debian@jff-webhosting.net> Fri, 12 Jun 2015 12:13:59 +0200
+
+argyll (1.7.0+repack-2) unstable; urgency=medium
+
+ * debian/patches/15_jam.patch:
+ - Rewrite rules to find libraries on all architectures.
+ * debian/control:
+ - Add zlib1g-dev to Build-Depends.
+
+ -- Jörg Frings-Fürst <debian@jff-webhosting.net> Mon, 04 May 2015 10:15:34 +0200
+
+argyll (1.7.0+repack-1) unstable; urgency=low
+
+ * Set package type to +repack.
+ * debian/control:
+ - Add libpng12-dev to build-Depends.
+ - Set Priority to optional.
+ - At package icc-utils and argyll-dbg set the Priority to extra.
+ * Make debian/man/scanin.1 more readable (LP: #1192368).
+ * Make build results reproducible:
+ - debian/rules:
+ + Replace build timestamps from ref files with date/time from
+ debian/changelog.
+ - debian/control:
+ + Add dpkg-dev to Build-Depends.
+ * debian/rules:
+ - get-orig-source:
+ + Remove unwanted Windows binaries.
+ + Remove unused sources: tiff, jpeg, zlib, png.
+ - Add ls2ti3 to build-manpages.
+ - New override_dh_compress:
+ + Don't compress html files.
+ * debian/copyright:
+ - Remove unused tags.
+ - Add new files.
+ - Set year 2015.
+ - Reorder files to prevend lintian warnings.
+ * Remove patches:
+ - 120_usb-db_new.patch applied upstream.
+ - 20_hurd_PATH_MAX.patch & 25_kfreebsd.patch not more needed.
+
+ -- Jörg Frings-Fürst <debian@jff-webhosting.net> Sun, 03 May 2015 19:36:05 +0200
+
+argyll (1.6.3-4) unstable; urgency=medium
+
+ * debian/control:
+ - On package argyll replace the wrong Replaces & Conflicts of argyll-bin
+ with the right Breaks & Replaces to icc-utils (<< 1.6.3-1).
+ (Closes: #767837)
+
+ -- Jörg Frings-Fürst <debian@jff-webhosting.net> Tue, 04 Nov 2014 07:59:20 +0100
+
+argyll (1.6.3-3) unstable; urgency=medium
+
+ * debian/control:
+ - Replace Architecture: any with linux-any to prevent KFreeBSD-* and
+ hurd-i386 from build during unusable usb system (Closes: #762773).
+
+ -- Jörg Frings-Fürst <debian@jff-webhosting.net> Sun, 05 Oct 2014 19:31:16 +0200
+
+argyll (1.6.3-2) unstable; urgency=medium
+
+ * debian/control:
+ - Bump Standards-Version to 3.9.6 (no changes required).
+ - Add Replaces: argyll (<< 1.6.3-1) and
+ Breaks (<< 1.6.3-1) to argyll-ref (Closes: #762780).
+ - At argyll move argyll-doc from Depends to Suggests (Closes: #762850).
+ - Remove from Build-Depends libusbhid-dev [kfreebsd-any].
+ - Remove ${shlibs:Depends} from Depends on argyll-doc and argyll-ref.
+ * debian/patches/15_jam.patch:
+ - To avoid use of the builtin libtiff and libjpeg add multiarch directories
+ for searching libraries and header files by using DEB_HOST_MULTIARCH
+ (Closes: #762772).
+ * New debian/patches/20_hurd_PATH_MAX.patch:
+ - Add missing PATH_MAX for hurd-i386 (Closes: #762774).
+ * debian/rules:
+ - Remove the move of the html docs into a subdirectory (Closes: #762771).
+ - Remove useless BUILTIN_TIFF=false from JAMCMDLINE.
+ - Remove "-A --link-doc=argyll-doc" from dh_installdocs (Closes: #762853).
+ * debian/*.(postinst|preinst|postrm):
+ - Replace dpkg-maintscript-helper dir_to_symlink to sysmlink_to_dir.
+ * Remove useless debian/argyll.examples
+ * Remove obsolete debian/patches/03_usb-db.diff.
+ * New debian/patches/120_usb-db_new.patch: (Closes: #762887)
+ - Instead of the obsolete usb-db use the builtin hwdb in the udev rules.
+ Thanks to Michael Biebl <biebl@debian.org>
+ * New debian/patches/25_kfreebsd.patch:
+ - Prevent build errors on kfreebsd-* (Closes: #762773).
+ Thanks to Steven Chamberlain <steven@pyro.eu.org>
+ * New debian/README.debian with notes to KFreeBSD-*.
+
+ -- Jörg Frings-Fürst <debian@jff-webhosting.net> Mon, 29 Sep 2014 09:22:15 +0200
+
+argyll (1.6.3-1) unstable; urgency=medium
+
+ * New Maintainer (Closes: #720178).
+ * New upstream release (Closes: #742658).
+ * debian/rules:
+ - Add get-orig-source.
+ - Remove useless --with quilt from dh $@
+ - Remove section for not included spyder2 firmware.
+ - Rewrite for use of upstream build system.
+ * debian/control:
+ - Set myself as maintainer.
+ - Update Build-Depends:
+ + Remove automake | automaken
+ - For previously not existing Vcs
+ + Create a new git repository on alioth.
+ + Add the fields Vcs-Browser and Vcs-Git.
+ - Change Priority from optional to extra.
+ - Remove useless packages:
+ + icc-utils
+ Now in argyll. Now only dummy package.
+ Change section to oldlibs
+ + libicc2 & libicc-dev
+ Useless. Only linked to argyll & icc-utils.
+ + libimdi0 & libimdi-dev
+ Useless. Only linked to argyll & icc-utils.
+ - Move documentation to new package argyll-doc
+ and symlink into the other packages.
+ - Add Pre-Depends:
+ * dpkg (>= 1.17.5) for use of the dpkg-maintscript-helper.
+ * debian/copyright:
+ - Rewrite into DEP-5 format.
+ - Add myself to the list of authors for debian/*.
+ - Add missing licenses and authors.
+ * debian/*.1
+ - Move to debian/man/*.1.
+ * debian/man/*
+ - Rewrite the help2man generated man pages (Closes: #670857)
+ * debian/patches/
+ - New 110_dispwin_segfault.patch to prevent segfault by
+ wrong parameter (Closes: #700253)
+ - Rewrite 03_usb-db.diff for new upstream release.
+ - Remove patches that included into new upstream release or useless:
+ + 01_autotools-support.diff
+ + 02_firmware-package-builder.diff
+ + 03_kfreebsd.diff
+ + 04_CVE-2012-4405.diff
+ + 05_external-yajl.diff
+ + 06_fix_udev_rule.patch
+ * New debian/*.(postinst|preinst|postrm):
+ - Remove existing doc directory and replace it with
+ a symlink to argyll-doc with dpkg-maintscript-helper dir_to_symlink.
+ * debian/argyll.preinst:
+ - Remove useless rm_conffile(). First version in Debian was 1.1.1-1,
+ rm_conffile was for version less equal 1.1.0-3.
+
+ -- Jörg Frings-Fürst <debian@jff-webhosting.net> Wed, 17 Sep 2014 08:47:26 +0200
+
+argyll (1.5.1-8) unstable; urgency=medium
+
+ * QA upload.
+ * Add a symbols file for libicc2 and libimdi0.
+
+ -- Iain Lane <laney@debian.org> Mon, 09 Jun 2014 12:52:11 +0100
+
+argyll (1.5.1-7) unstable; urgency=medium
+
+ * QA upload.
+ * debian/control: Bump udev recommended version to >= 196
+
+ -- Laurent Bigonville <bigon@debian.org> Sun, 04 May 2014 22:30:57 +0200
+
+argyll (1.5.1-6) unstable; urgency=medium
+
+ * QA upload.
+ * debian/patches/03_usb-db.diff: Use hwdb builtin, instead of the obsolete
+ usb-db in the udev rules.
+ * debian/patches/06_fix_udev_rule.patch: Fix udev rules to actually work;
+ ENV{ACL_MANAGE} has stopped working ages ago, and with logind it's now the
+ "uaccess" tag. Dropping also consolekit from Recommends. (Closes: #746974)
+ * debian/rules: Add libpam-systemd as a recommends to be sure we have a
+ session registered, this is needed for the permissions on the devices.
+ * debian/control: Build-depend on libtiff-dev rather than libtiff4-dev.
+ (Closes: #733126)
+ * debian/control: Drop Uploaders as the package has been orphaned
+ * debian/control: Bump Standards-Version to 3.9.5 (no further changes)
+
+ -- Laurent Bigonville <bigon@debian.org> Sun, 04 May 2014 21:36:24 +0200
+
+argyll (1.5.1-5) unstable; urgency=low
+
+ * Package orphaned. I don't intent to support the work of an agressive
+ upstream author more longer and realy good luck for the next maintainer.
+
+ -- Christian Marillat <marillat@debian.org> Mon, 19 Aug 2013 14:18:29 +0200
+
+argyll (1.5.1-4) unstable; urgency=low
+
+ * Revert previous yajl patch this break argyll.
+ * Regenerate all manpages.
+ * Add colord and gir1.2-colordgtk-1.0 in Suggests.
+ * Don't package .a and .la files.
+
+ -- Christian Marillat <marillat@debian.org> Mon, 12 Aug 2013 15:51:54 +0200
+
+argyll (1.5.1-3) unstable; urgency=low
+
+ * Uptade patch 01_autotools-support with patch from bug report to link
+ argyll against external yajl also add an one line patch called
+ 05_external-yajl (Closes: #544223)
+
+ -- Christian Marillat <marillat@debian.org> Fri, 02 Aug 2013 08:10:38 +0200
+
+argyll (1.5.1-2) unstable; urgency=low
+
+ * Package iccdump.1 and icclu.1 only in icc-utils package
+ (Closes: #717990, # 717993).
+
+ -- Christian Marillat <marillat@debian.org> Sat, 27 Jul 2013 21:43:59 +0200
+
+argyll (1.5.1-1) unstable; urgency=low
+
+ * New upstream release.
+ * Removed 03_kfreebsd patch.
+ * Refresh 01_autotools-support patch (CLoses: 713545)
+ * New patch 03_usb-db to add support for newer udev (Closes: 717504)
+
+ -- Christian Marillat <marillat@debian.org> Fri, 26 Jul 2013 16:25:19 +0200
+
+argyll (1.4.0-8) unstable; urgency=low
+
+ * Add a Breaks field and update the version in the Replaces field for the
+ icc-utils package (Closes: #694287)
+
+ -- Christian Marillat <marillat@debian.org> Sun, 25 Nov 2012 09:59:35 +0100
+
+argyll (1.4.0-7) unstable; urgency=high
+
+ * New patch 04_CVE-2012-4405.diff to fix CVE-2012-4405 issue
+ (Closes: #687275)
+
+ -- Christian Marillat <marillat@debian.org> Tue, 11 Sep 2012 13:45:12 +0200
+
+argyll (1.4.0-6) unstable; urgency=low
+
+ * Use dh_autoreconf (Closes: #678909)
+
+ -- Christian Marillat <marillat@debian.org> Mon, 25 Jun 2012 07:52:56 +0200
+
+argyll (1.4.0-5) unstable; urgency=low
+
+ * Refresh 01_autotools-support patch (Closes: #678750).
+
+ -- Christian Marillat <marillat@debian.org> Mon, 25 Jun 2012 00:01:31 +0200
+
+argyll (1.4.0-4) unstable; urgency=low
+
+ * Should Build-Depends on libusb-dev (Closes: #670329).
+
+ -- Christian Marillat <marillat@debian.org> Wed, 25 Apr 2012 07:46:07 +0200
+
+argyll (1.4.0-3) unstable; urgency=low
+
+ * Fix kfreebsd build. Thanks to Robert Millan for the patch.
+ (Closes: #595951 #630208).
+
+ -- Christian Marillat <marillat@debian.org> Tue, 24 Apr 2012 07:49:03 +0200
+
+argyll (1.4.0-2) unstable; urgency=low
+
+ * Move binaries from libicc2 package to a new icc-utils package (Closes:
+ #670003).
+
+ -- Christian Marillat <marillat@debian.org> Sun, 22 Apr 2012 16:07:43 +0200
+
+argyll (1.4.0-1) unstable; urgency=low
+
+ * New upstream release.
+
+ -- Christian Marillat <marillat@debian.org> Sat, 21 Apr 2012 15:53:50 +0200
+
+argyll (1.3.7-1.1) unstable; urgency=low
+
+ * Use DEB_LDFLAGS_MAINT_APPEND to add more LDFLAGS (Closes: #667924)
+
+ -- Christian Marillat <marillat@debian.org> Mon, 09 Apr 2012 10:45:56 +0200
+
+argyll (1.3.7-1) unstable; urgency=low
+
+ * New upstream release.
+ * Try to fix the build (Thanks to Dmitry Nezhevenko) (Closes: #665055).
+ * Move to debhelper 9 for multi-arch and hardened flags.
+
+ -- Christian Marillat <marillat@debian.org> Tue, 03 Apr 2012 08:46:37 +0200
+
+argyll (1.3.6-3) unstable; urgency=low
+
+ * Call dh_quilt_unpatch after MAKE distclean.
+ * Rework Makefile test to be sure we don't call upstream Makefile with
+ missing distclean target.
+
+ -- Christian Marillat <marillat@debian.org> Tue, 20 Mar 2012 14:44:40 +0100
+
+argyll (1.3.6-2) unstable; urgency=low
+
+ * debian/rules Fix clean target.
+
+ -- Christian Marillat <marillat@debian.org> Mon, 19 Mar 2012 16:50:40 +0100
+
+argyll (1.3.6-1) unstable; urgency=low
+
+ * New upstream release.
+ * Removed 03_add-ColorHug_sensor_driver patch, but beware the support for
+ ColorHug is experimental.
+
+ -- Christian Marillat <marillat@debian.org> Mon, 19 Mar 2012 10:55:20 +0100
+
+argyll (1.3.5-7) unstable; urgency=low
+
+ * Really fix previous bug.
+ * Use debhelper 9.
+
+ -- Christian Marillat <marillat@debian.org> Tue, 13 Mar 2012 08:41:01 +0100
+
+argyll (1.3.5-6) unstable; urgency=low
+
+ * Fix another bug in ColorHugColorHug (Closes: #661643)
+
+ -- Christian Marillat <marillat@debian.org> Sun, 11 Mar 2012 16:59:06 +0100
+
+argyll (1.3.5-5) unstable; urgency=low
+
+ * Fix two bugs in ColorHugColorHug patch (Closes: #657582).
+
+ -- Christian Marillat <marillat@debian.org> Sat, 28 Jan 2012 01:42:30 +0100
+
+argyll (1.3.5-4) unstable; urgency=low
+
+ * New patch 03_add-ColorHug_sensor_driver to add support for the ColorHug
+ sensor (Closes: #655888)
+
+ -- Christian Marillat <marillat@debian.org> Tue, 17 Jan 2012 18:22:51 +0100
+
+argyll (1.3.5-3) unstable; urgency=low
+
+ * debian/rules Enable make distclean (Closes: #649728).
+
+ -- Christian Marillat <marillat@debian.org> Wed, 23 Nov 2011 15:17:42 +0100
+
+argyll (1.3.5-2) unstable; urgency=low
+
+ * Don't package icclu and iccdump binaries in argyll, files already in
+ libicc2 package.
+
+ -- Christian Marillat <marillat@debian.org> Mon, 21 Nov 2011 08:25:40 +0100
+
+argyll (1.3.5-1) unstable; urgency=low
+
+ [ Christian Marillat ]
+ * New maintainer (Closes: #622620) Thanks, Roland!
+ * Use dh-autoreconf.
+
+ [ Dmitry Nezhevenko ]
+ * New upstream release (Closes: #647093).
+ * Switch to dpkg-source 3.0 (quilt) format.
+ * Merge debian changes from 1.3.0 (experimental) and 1.1.1 (unstable).
+ * Unsplit libicc2, built from the same source package again (since
+ upstream only ships it as part of Argyll anyway). Actually was already
+ done in experimental (Closes: #636801).
+ * Use kfreebsd-any instead of kfreebsd-(i386|amd64) to match kFreeBSD
+ (Closes: #634688).
+ * Provide dedicated libimdi0/libimdi-dev libraries (Closes: #611139).
+ * Add argyll-dbg package with debug symbols.
+
+ -- Christian Marillat <marillat@debian.org> Sun, 13 Nov 2011 15:30:42 +0100
+
+argyll (1.3.0-3) experimental; urgency=low
+
+ * Minor future-proofing in debian/rules.
+ * Also removed redundant shipping of the firmware-package-builder
+ tarball, thanks to Pascal de Bruijn for noticing.
+
+ -- Roland Mas <lolando@debian.org> Fri, 17 Sep 2010 11:05:22 +0200
+
+argyll (1.3.0-2) experimental; urgency=low
+
+ * Fixed packaging bugs introduced when un-splitting the source package.
+
+ -- Roland Mas <lolando@debian.org> Tue, 14 Sep 2010 11:29:27 +0200
+
+argyll (1.3.0-1) experimental; urgency=low
+
+ * New upstream release.
+
+ -- Roland Mas <lolando@debian.org> Wed, 08 Sep 2010 15:16:29 +0200
+
+argyll (1.2.1-1) experimental; urgency=low
+
+ * New upstream bugfix release.
+ * Please welcome Xavier Oswald as uploader.
+
+ -- Roland Mas <lolando@debian.org> Sun, 05 Sep 2010 15:50:41 +0200
+
+argyll (1.2.0-0) UNRELEASED; urgency=low
+
+ * New upstream release.
+ * Unsplit libicc2, built from the same source package again (since
+ upstream only ships it as part of Argyll anyway).
+ * Bumped Standards-Version to 3.9.1 (no changes).
+ * argyll-firmware-spyder2: Moved firmware to /usr/share/color, which is
+ freedesktop.org-compliant and (more to the point) where Argyll 1.2.0
+ looks for it.
+
+ -- Roland Mas <lolando@debian.org> Thu, 05 Aug 2010 15:33:20 +0200
+
+argyll (1.1.1-2) unstable; urgency=medium
+
+ * QA upload.
+ * Add ../libargyll.la to LDADD (Closes: #615692).
+ * Don't ship .la files (Closes: #621142).
+
+ -- Luk Claes <luk@debian.org> Sat, 11 Jun 2011 11:18:43 +0200
+
+argyll (1.1.1-1) unstable; urgency=low
+
+ * New upstream release.
+ * Switched dependency on policykit-1 to a more correct recommendation on
+ consolekit + recent udev.
+ * Updated copyright file (doc is now GFDL-1.3).
+ * Bumped Standards-Version to 3.8.4 (no changes required).
+
+ -- Roland Mas <lolando@debian.org> Wed, 24 Feb 2010 21:26:55 +0100
+
+argyll (1.1.0-5) unstable; urgency=low
+
+ * Bumped version build-dependency on libicc-dev to prevent segmentation
+ fault.
+
+ -- Roland Mas <lolando@debian.org> Mon, 25 Jan 2010 19:06:07 +0100
+
+argyll (1.1.0-4) unstable; urgency=low
+
+ * Adapted packaging to new udev rules, now compatible with PolicyKit-1
+ (closes: #529411). This should fix device file permissions problems,
+ too (closes: #549406). And also the "deprecated udev function"
+ warning (closes: #564269).
+ * Removed conffiles no longer shipped by the package.
+
+ -- Roland Mas <lolando@debian.org> Mon, 25 Jan 2010 13:47:59 +0100
+
+argyll (1.1.0-3) unstable; urgency=low
+
+ * Only depend on udev for Linux systems.
+
+ -- Roland Mas <lolando@debian.org> Mon, 25 Jan 2010 11:42:54 +0100
+
+argyll (1.1.0-2) unstable; urgency=low
+
+ * Fix build on FreeBSD, patch from Petr Salinger
+ <Petr.Salinger@seznam.cz> (closes: #566768).
+
+ -- Roland Mas <lolando@debian.org> Mon, 25 Jan 2010 10:29:33 +0100
+
+argyll (1.1.0-1) unstable; urgency=low
+
+ * New upstream release, including a patch obtained from upstream just
+ after the actual release.
+ * Also, stop using the system's libusb, which causes known problems with
+ Argyll. Now using Argyll's patched copy (privately, since the patches
+ cause problems with other software).
+
+ -- Roland Mas <lolando@debian.org> Sun, 24 Jan 2010 23:34:18 +0100
+
+argyll (1.1.0~rc4-1) unstable; urgency=low
+
+ * New upstream pre-release.
+ * Ship all doc files with a wildcard rule, rather than a fixed (and
+ outdated) set.
+
+ -- Roland Mas <lolando@debian.org> Wed, 06 Jan 2010 10:33:54 +0100
+
+argyll (1.1.0~rc3-1) unstable; urgency=low
+
+ * New upstream pre-release.
+ * Moved udev rules file to /lib/udev/rules.d.
+
+ -- Roland Mas <lolando@debian.org> Tue, 05 Jan 2010 14:49:38 +0100
+
+argyll (1.1.0~rc2-1) unstable; urgency=low
+
+ * New upstream pre-release.
+ * Updated location of Bazaar branches in control file.
+ * The Debian-specific branch now feeds from the "midstream" branch
+ rather than the "upstream-releases" branch, to ease collaboration with
+ other distributions. This shouldn't make any difference on the
+ package contents.
+
+ -- Roland Mas <lolando@debian.org> Thu, 10 Dec 2009 17:26:04 +0100
+
+argyll (1.1.0~rc1+dfsg-1) unstable; urgency=low
+
+ * Removed non-free IETF RFC/I-D from source package, thanks to Simon
+ Josefsson (closes: #555377).
+
+ -- Roland Mas <lolando@debian.org> Sat, 14 Nov 2009 20:23:53 +0100
+
+argyll (1.1.0~rc1-3) unstable; urgency=low
+
+ * Added Build-Depends: libusbhid-dev for kFreeBSD architectures.
+
+ -- Roland Mas <lolando@debian.org> Mon, 09 Nov 2009 13:34:54 +0100
+
+argyll (1.1.0~rc1-2) unstable; urgency=low
+
+ * Fixed build on GNU/kFreeBSD ports.
+ * Applied patch from upstream that fixes an infinite loop if Xrandr is
+ enabled.
+
+ -- Roland Mas <lolando@debian.org> Mon, 09 Nov 2009 11:46:57 +0100
+
+argyll (1.1.0~rc1-1) unstable; urgency=low
+
+ * New upstream pre-release.
+ * Updated debian/copyright (upstream switched to Affero GPLv3).
+ * Bumped versioned build-dependency on debhelper.
+ * Bumped standards version to 3.8.3 (no changes needed).
+
+ -- Roland Mas <lolando@debian.org> Sun, 08 Nov 2009 22:19:45 +0100
+
+argyll (1.0.4-1) unstable; urgency=low
+
+ * New upstream release.
+
+ -- Roland Mas <lolando@debian.org> Fri, 03 Jul 2009 14:14:11 +0200
+
+argyll (1.0.3+dfsg1-3) unstable; urgency=low
+
+ * libicc is now a separate source+binary package, so argyll now links
+ against it.
+ * Since that new libicc is a proper new upstream snapshot, the problems
+ introduced with the local patches should be fixed (closes: #524478).
+
+ -- Roland Mas <lolando@debian.org> Tue, 02 Jun 2009 11:26:01 +0200
+
+argyll (1.0.3+dfsg1-2) unstable; urgency=low
+
+ * Adapted debian/watch file to match version mangling.
+ * Updated autotools build system to dynamically link against the
+ internal libraries and provide a libicc.so for dynamic linking.
+ * Also ship the corresponding header files.
+ * Not splitting libicc into its own binary package just yet though, I
+ want to get more testing of the dynamic linking first.
+
+ -- Roland Mas <lolando@debian.org> Thu, 14 May 2009 16:49:44 +0200
+
+argyll (1.0.3+dfsg1-1) unstable; urgency=low
+
+ * argyll-firmware-spyder2 is now generated as section non-free/graphics
+ rather than just graphics.
+ * Removed non-free RFC from source package (closes: #524972).
+
+ -- Roland Mas <lolando@debian.org> Tue, 28 Apr 2009 11:47:38 +0200
+
+argyll (1.0.3-5) unstable; urgency=low
+
+ * Documented web interface for the Bazaar repository.
+ * Added source package for the Spyder2 firmware.
+
+ -- Roland Mas <lolando@debian.org> Fri, 17 Apr 2009 23:34:40 +0200
+
+argyll (1.0.3-4) unstable; urgency=low
+
+ * Actually create /var/lib/argyll so spyd2en can store its firmware in
+ it... also remove it on purge.
+ * Also look for the Spyder2 firmware in /lib/firmware.
+ * Shortened debian/rules quite a bit thanks to Debhelper 7.
+ * Fixed build rules so testsuite passes.
+ * Bumped standards-version to 3.8.1 (no changes).
+
+ -- Roland Mas <lolando@debian.org> Thu, 16 Apr 2009 11:25:26 +0200
+
+argyll (1.0.3-3) unstable; urgency=low
+
+ * Another patch for icclib, this time from Jan Lieskovsky, fixing some
+ more vulnerabilities described in CVE-2009-0792 (closes: #523472).
+
+ -- Roland Mas <lolando@debian.org> Fri, 10 Apr 2009 17:53:55 +0200
+
+argyll (1.0.3-2) unstable; urgency=low
+
+ * Patched embedded copy of icclib to fix integer overflow and denial of
+ service vulnerabilities as described in CVE-2009-0583 and
+ CVE-2009-0584. Patch provided by Moritz Muehlenhoff and the Debian
+ security team (closes: #522448).
+
+ -- Roland Mas <lolando@debian.org> Fri, 03 Apr 2009 22:43:14 +0200
+
+argyll (1.0.3-1) unstable; urgency=low
+
+ * New upstream release.
+ * Adopted package from Christian Marillat's Debian-Multimedia
+ repository (closes: #498396). Thanks, Christian!
+ * Stopped removing some files in clean target.
+ * Switched build-system to autotools.
+ * Link to system libusb rather than locally-shipped one.
+ * Fixed watch file.
+ * Moved Spyder2 firmware to /var/lib/argyll.
+ * Added detailed debian/copyright file.
+ * Use dh_prep instead of dh_clean -k.
+ * Rename /usr/bin/foo to /usr/bin/argyll-foo, for foo in
+ {average,refine,targen,verify}, to avoid having binaries with too
+ generic names. Documented in README.Debian.
+
+ -- Roland Mas <lolando@debian.org> Thu, 19 Feb 2009 20:34:48 +0100
+
+argyll (1.0.0-0.0) unstable; urgency=low
+
+ * New upstream release.
+ * Added configuration files for udev, hal and policykit.
+
+ -- Christian Marillat <marillat@debian.org> Fri, 18 Jul 2008 08:51:45 +0200
+
+argyll (0.60-0.1) unstable; urgency=low
+
+ * Need to Build-depends on libxinerama-dev instead of x11proto-xinerama-dev.
+
+ -- Christian Marillat <marillat@debian.org> Fri, 21 Jul 2006 06:25:46 +0200
+
+argyll (0.60-0.0) unstable; urgency=low
+
+ * New upstream release.
+
+ -- Christian Marillat <marillat@debian.org> Thu, 20 Jul 2006 10:56:43 +0200
+
+argyll (0.53.1-0.2) unstable; urgency=low
+
+ * Apply a patch from Guido to fix build under ppc.
+
+ -- Christian Marillat <marillat@debian.org> Tue, 23 May 2006 14:40:45 +0200
+
+argyll (0.53.1-0.1) unstable; urgency=low
+
+ * Add libxxf86vm-dev and x11proto-xf86vidmode-dev in build-Depends.
+
+ -- Christian Marillat <marillat@debian.org> Fri, 12 May 2006 16:27:46 +0200
+
+argyll (0.53.1-0.0) unstable; urgency=low
+
+ * New upstream release.
+
+ -- Christian Marillat <marillat@debian.org> Thu, 9 Feb 2006 13:56:03 +0100
+
+argyll (0.53-0.1) unstable; urgency=low
+
+ * Rename icclink in icclink-argyll.
+ * Rename sprof in sprof-argyll.
+
+ -- Christian Marillat <marillat@debian.org> Wed, 8 Feb 2006 11:42:15 +0100
+
+argyll (0.53-0.0) unstable; urgency=low
+
+ * Initial release.
+
+ -- Christian Marillat <marillat@debian.org> Sun, 5 Feb 2006 22:08:11 +0100
diff --git a/debian/compat b/debian/compat
new file mode 100644
index 0000000..f599e28
--- /dev/null
+++ b/debian/compat
@@ -0,0 +1 @@
+10
diff --git a/debian/control b/debian/control
new file mode 100644
index 0000000..87ca9c9
--- /dev/null
+++ b/debian/control
@@ -0,0 +1,65 @@
+Source: argyll
+Section: graphics
+Priority: optional
+Maintainer: Jörg Frings-Fürst <debian@jff.email>
+Standards-Version: 4.1.2.0
+Build-Depends:
+ debhelper (>= 10),
+ jam,
+ libjpeg-dev,
+ libpng-dev,
+ libssl-dev,
+ libtiff5-dev,
+ libtool,
+ libusb-1.0-0-dev,
+ libx11-dev,
+ libxinerama-dev,
+ libxrandr-dev,
+ libxss-dev,
+ libxxf86vm-dev,
+ x11proto-scrnsaver-dev,
+ x11proto-xf86vidmode-dev,
+ zlib1g-dev
+Homepage: https://www.argyllcms.com/
+Vcs-Git: git://anonscm.debian.org/collab-maint/argyll.git
+Vcs-Browser: https://anonscm.debian.org/cgit/collab-maint/argyll.git
+
+Package: argyll
+Architecture: linux-any
+Depends: ${misc:Depends}, ${shlibs:Depends}, argyll-ref
+Recommends: libpam-systemd [linux-any], udev [linux-any]
+Suggests: argyll-doc, colord, gir1.2-colordgtk-1.0
+Description: Color Management System, calibrator and profiler
+ Argyll is an experimental, open source, ICC compatible color management
+ system. It supports accurate ICC profile creation for scanners, CMYK
+ printers, film recorders and calibration and profiling of displays.
+ Spectral sample data is supported, allowing a selection of illuminants
+ observer types, and paper fluorescent whitener additive compensation.
+ Profiles can also incorporate source specific gamut mappings for perceptual
+ and saturation intents. Gamut mapping and profile linking uses the CIECAM02
+ appearance model, a unique gamut mapping algorithm, and a wide selection of
+ rendering intents. It also includes code for the fastest portable 8 bit
+ raster color conversion engine available anywhere, as well as support for
+ fast, fully accurate 16 bit conversion. Device color gamuts can also be
+ viewed and compared using a VRML viewer.
+
+Package: argyll-ref
+Architecture: all
+Depends: ${misc:Depends}
+Description: Color Management System, calibrator and profiler (data files)
+ Argyll is an experimental, open source, ICC compatible color management
+ system. It supports accurate ICC profile creation for scanners, CMYK
+ printers, film recorders and calibration and profiling of displays.
+ .
+ This package contains the data files for argyll.
+
+Package: argyll-doc
+Architecture: all
+Section: doc
+Depends: ${misc:Depends}
+Description: Color Management System, calibrator and profiler (documentation)
+ Argyll is an experimental, open source, ICC compatible color management
+ system. It supports accurate ICC profile creation for scanners, CMYK
+ printers, film recorders and calibration and profiling of displays.
+ .
+ This package contains the documentation for argyll.
diff --git a/debian/copyright b/debian/copyright
new file mode 100644
index 0000000..3c5624a
--- /dev/null
+++ b/debian/copyright
@@ -0,0 +1,977 @@
+Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
+Upstream-Name: ArgyllCMS
+Upstream-Contact: Graeme W. Gill <Graeme@argyllcms.com>
+Source: https://www.argyllcms.com/
+Files-Excluded: yajl/yajl_test.exe
+ yajl/yajl_test.obj
+ usb/bin/*
+ jpeg/*
+ tiff/*
+ zlib/*
+ png/*
+
+Files: *
+Copyright: 1995-2016 Graeme W. Gill <Graeme@argyllcms.com>
+License: AGPL-3
+
+Files: icc/*
+ cgats/*
+ jcnf/*
+ ucmm/*
+Copyright: 1995-2016 Graeme W. Gill <Graeme@argyllcms.com>
+License: Expat
+
+Files: ccast/cast_channel.proto
+Copyright: 2013 The Chromium Authors
+License: BSD-3
+
+Files: ccast/chan/protobuf-c.*
+Copyright: 2008-2014 Dave Benson and the protobuf-c authors
+License: BSD-2
+
+Files: ccast/axTLS/*.c
+ ccast/axTLS/*.h
+Copyright: 2007 Cameron Rich
+License: BSD-3
+
+Files: ccast/axTLS/os_int.h
+Copyright: 2014 Graeme W. Gill
+License: Expat
+
+Files: icc/iccV42.h
+Copyright: 1994-1998 SunSoft, Inc
+ 1994-2015 Graeme W. Gill <Graeme@argyllcms.com>
+License: Expat
+
+Files: numlib/quadprog.*
+Copyright : 2007-2016 Luca Di Gaspero
+ 2017 Graeme W. Gill <Graeme@argyllcms.com>
+License: MIT
+
+Files: plot/x3dom.*
+Copyright: 2009 Fraunhofer IGD, Darmstadt, Germany
+License: MIT and GPL-3+
+
+Files: yajl/*
+Copyright: 2007-2014 Lloyd Hilaiel
+License: BSD-3
+
+Files: yajl/yajl_tree.*
+Copyright: 2010-2011 Florian Forster <ff@octo.it>
+License: BSD-3
+
+Files: usb/*
+Copyright: 2012-2015 Graeme W. Gill <Graeme@argyllcms.com>
+License: Expat
+
+Files: usb/driver/*
+Copyright: 2002-2005 Stephan Meyer <ste_meyer@web.de>
+License: GPL-2 or LGPL-2
+ The library (DLL) is distributed under the terms of the GNU Lesser
+ General Public License (LGPL).
+ .
+ All other components (drivers, services, installer) are distributed
+ under the terms of the GNU General Public License (GPL).
+
+Files: usb/driver/ioctl.c
+Copyright: 2002-2005 Stephan Meyer <ste_meyer@web.de>
+ 2010 Travis Robinson <libusbdotnet@gmail.com>
+License: GPL-2+
+
+Files: usb/driver/transfer.c
+Copyright: 2002-2005 Stephan Meyer <ste_meyer@web.de>
+ 2010 Travis Robinson <libusbdotnet@gmail.com>
+License: GPL-3+
+
+Files: xicc/iccjpeg.*
+Copyright: 1998-2010 Marti Maria Saguer
+License: Expat
+
+Files: spectro/*
+Copyright: 1996-2016 Graeme W. Gill <Graeme@argyllcms.com>
+License: GPL-2+
+
+Files: spectro/dispcal.c
+ spectro/dispsup.c
+ spectro/dispsup.h
+ spectro/dispwin.c
+ spectro/dispwin.h
+ spectro/average.c
+ spectro/synthcal.c
+ spectro/synthread.c
+ spectro/ccxxmake.c
+ spectro/fakeread.c
+ spectro/License.txt
+ spectro/chartread.c
+ spectro/dispread.c
+ spectro/illumread.c
+Copyright: 1996-2015 Graeme W. Gill <Graeme@argyllcms.com>
+License: AGPL-3
+
+Files: spectro/colorhug.*
+Copyright: 2006-2015 Graeme W. Gill <Graeme@argyllcms.com>
+ 2011 Richard Hughes
+License: GPL-2+
+
+Files: spectro/mongoose.*
+Copyright: 2004-2012 Sergey Lyubka
+License: Expat
+
+Files: spectro/spec2cie.c
+Copyright: 2005 Gerhard Fuernkranz
+ 2006-2015 Graeme W. Gill <Graeme@argyllcms.com>
+License: GPL-2+
+
+Files: h/sort.h
+Copyright: 1996-2010 Graeme W. Gill <Graeme@argyllcms.com>
+License: GPL-2+
+
+Files: xicc/xspect.c
+Copyright: 2000-2015 Graeme W. Gill <Graeme@argyllcms.com>
+License: GPL-2+
+
+Files: doc/*
+Copyright: 1995-2015 Graeme W. Gill <Graeme@argyllcms.com>
+License: GFDL-1.3+
+
+Files: numlib/numsup.*
+Copyright: 1997-2015 Graeme W. Gill <Graeme@argyllcms.com>
+License: GPL-2+
+
+Files: rspl/rspl1.*
+Copyright: 1996-2010 Graeme W. Gill <Graeme@argyllcms.com>
+License: GPL-2+
+
+Files: xml/*
+Copyright: 2003-2011 Michael R Sweet
+License: LGPL-2+
+
+Files: xml/install-sh
+Copyright: 1991 Massachusetts Institute of Technology
+License: MIT
+
+Files: ref/*.icm
+ icc/*.icm
+Copyright: Public domain
+License: public-domain
+ Public Domain. No Warranty, Use at own risk
+
+Files: debian/*
+Copyright: 2006-2008 Christian Marillat <marillat@debian.org>
+ 2008-2010 Roland Mas <lolando@debian.org>
+ 2014-2017 Jörg Frings-Fürst <debian@jff.email>
+License: GPL-3+
+
+License: AGPL-3
+ GNU AFFERO GENERAL PUBLIC LICENSE
+ Version 3, 19 November 2007
+ .
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+ .
+ Preamble
+ .
+ The GNU Affero General Public License is a free, copyleft license for
+ software and other kinds of works, specifically designed to ensure
+ cooperation with the community in the case of network server software.
+ .
+ The licenses for most software and other practical works are designed
+ to take away your freedom to share and change the works. By contrast,
+ our General Public Licenses are intended to guarantee your freedom to
+ share and change all versions of a program--to make sure it remains free
+ software for all its users.
+ .
+ When we speak of free software, we are referring to freedom, not
+ price. Our General Public Licenses are designed to make sure that you
+ have the freedom to distribute copies of free software (and charge for
+ them if you wish), that you receive source code or can get it if you
+ want it, that you can change the software or use pieces of it in new
+ free programs, and that you know you can do these things.
+ .
+ Developers that use our General Public Licenses protect your rights
+ with two steps: (1) assert copyright on the software, and (2) offer
+ you this License which gives you legal permission to copy, distribute
+ and/or modify the software.
+ .
+ A secondary benefit of defending all users' freedom is that
+ improvements made in alternate versions of the program, if they
+ receive widespread use, become available for other developers to
+ incorporate. Many developers of free software are heartened and
+ encouraged by the resulting cooperation. However, in the case of
+ software used on network servers, this result may fail to come about.
+ The GNU General Public License permits making a modified version and
+ letting the public access it on a server without ever releasing its
+ source code to the public.
+ .
+ The GNU Affero General Public License is designed specifically to
+ ensure that, in such cases, the modified source code becomes available
+ to the community. It requires the operator of a network server to
+ provide the source code of the modified version running there to the
+ users of that server. Therefore, public use of a modified version, on
+ a publicly accessible server, gives the public access to the source
+ code of the modified version.
+ .
+ An older license, called the Affero General Public License and
+ published by Affero, was designed to accomplish similar goals. This is
+ a different license, not a version of the Affero GPL, but Affero has
+ released a new version of the Affero GPL which permits relicensing under
+ this license.
+ .
+ The precise terms and conditions for copying, distribution and
+ modification follow.
+ .
+ TERMS AND CONDITIONS
+ .
+ 0. Definitions.
+ .
+ "This License" refers to version 3 of the GNU Affero General Public License.
+ .
+ "Copyright" also means copyright-like laws that apply to other kinds of
+ works, such as semiconductor masks.
+ .
+ "The Program" refers to any copyrightable work licensed under this
+ License. Each licensee is addressed as "you". "Licensees" and
+ "recipients" may be individuals or organizations.
+ .
+ To "modify" a work means to copy from or adapt all or part of the work
+ in a fashion requiring copyright permission, other than the making of an
+ exact copy. The resulting work is called a "modified version" of the
+ earlier work or a work "based on" the earlier work.
+ .
+ A "covered work" means either the unmodified Program or a work based
+ on the Program.
+ .
+ To "propagate" a work means to do anything with it that, without
+ permission, would make you directly or secondarily liable for
+ infringement under applicable copyright law, except executing it on a
+ computer or modifying a private copy. Propagation includes copying,
+ distribution (with or without modification), making available to the
+ public, and in some countries other activities as well.
+ .
+ To "convey" a work means any kind of propagation that enables other
+ parties to make or receive copies. Mere interaction with a user through
+ a computer network, with no transfer of a copy, is not conveying.
+ .
+ An interactive user interface displays "Appropriate Legal Notices"
+ to the extent that it includes a convenient and prominently visible
+ feature that (1) displays an appropriate copyright notice, and (2)
+ tells the user that there is no warranty for the work (except to the
+ extent that warranties are provided), that licensees may convey the
+ work under this License, and how to view a copy of this License. If
+ the interface presents a list of user commands or options, such as a
+ menu, a prominent item in the list meets this criterion.
+ .
+ 1. Source Code.
+ .
+ The "source code" for a work means the preferred form of the work
+ for making modifications to it. "Object code" means any non-source
+ form of a work.
+ .
+ A "Standard Interface" means an interface that either is an official
+ standard defined by a recognized standards body, or, in the case of
+ interfaces specified for a particular programming language, one that
+ is widely used among developers working in that language.
+ .
+ The "System Libraries" of an executable work include anything, other
+ than the work as a whole, that (a) is included in the normal form of
+ packaging a Major Component, but which is not part of that Major
+ Component, and (b) serves only to enable use of the work with that
+ Major Component, or to implement a Standard Interface for which an
+ implementation is available to the public in source code form. A
+ "Major Component", in this context, means a major essential component
+ (kernel, window system, and so on) of the specific operating system
+ (if any) on which the executable work runs, or a compiler used to
+ produce the work, or an object code interpreter used to run it.
+ .
+ The "Corresponding Source" for a work in object code form means all
+ the source code needed to generate, install, and (for an executable
+ work) run the object code and to modify the work, including scripts to
+ control those activities. However, it does not include the work's
+ System Libraries, or general-purpose tools or generally available free
+ programs which are used unmodified in performing those activities but
+ which are not part of the work. For example, Corresponding Source
+ includes interface definition files associated with source files for
+ the work, and the source code for shared libraries and dynamically
+ linked subprograms that the work is specifically designed to require,
+ such as by intimate data communication or control flow between those
+ subprograms and other parts of the work.
+ .
+ The Corresponding Source need not include anything that users
+ can regenerate automatically from other parts of the Corresponding
+ Source.
+ .
+ The Corresponding Source for a work in source code form is that
+ same work.
+ .
+ 2. Basic Permissions.
+ .
+ All rights granted under this License are granted for the term of
+ copyright on the Program, and are irrevocable provided the stated
+ conditions are met. This License explicitly affirms your unlimited
+ permission to run the unmodified Program. The output from running a
+ covered work is covered by this License only if the output, given its
+ content, constitutes a covered work. This License acknowledges your
+ rights of fair use or other equivalent, as provided by copyright law.
+ .
+ You may make, run and propagate covered works that you do not
+ convey, without conditions so long as your license otherwise remains
+ in force. You may convey covered works to others for the sole purpose
+ of having them make modifications exclusively for you, or provide you
+ with facilities for running those works, provided that you comply with
+ the terms of this License in conveying all material for which you do
+ not control copyright. Those thus making or running the covered works
+ for you must do so exclusively on your behalf, under your direction
+ and control, on terms that prohibit them from making any copies of
+ your copyrighted material outside their relationship with you.
+ .
+ Conveying under any other circumstances is permitted solely under
+ the conditions stated below. Sublicensing is not allowed; section 10
+ makes it unnecessary.
+ .
+ 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+ .
+ No covered work shall be deemed part of an effective technological
+ measure under any applicable law fulfilling obligations under article
+ 11 of the WIPO copyright treaty adopted on 20 December 1996, or
+ similar laws prohibiting or restricting circumvention of such
+ measures.
+ .
+ When you convey a covered work, you waive any legal power to forbid
+ circumvention of technological measures to the extent such circumvention
+ is effected by exercising rights under this License with respect to
+ the covered work, and you disclaim any intention to limit operation or
+ modification of the work as a means of enforcing, against the work's
+ users, your or third parties' legal rights to forbid circumvention of
+ technological measures.
+ .
+ 4. Conveying Verbatim Copies.
+ .
+ You may convey verbatim copies of the Program's source code as you
+ receive it, in any medium, provided that you conspicuously and
+ appropriately publish on each copy an appropriate copyright notice;
+ keep intact all notices stating that this License and any
+ non-permissive terms added in accord with section 7 apply to the code;
+ keep intact all notices of the absence of any warranty; and give all
+ recipients a copy of this License along with the Program.
+ .
+ You may charge any price or no price for each copy that you convey,
+ and you may offer support or warranty protection for a fee.
+ .
+ 5. Conveying Modified Source Versions.
+ .
+ You may convey a work based on the Program, or the modifications to
+ produce it from the Program, in the form of source code under the
+ terms of section 4, provided that you also meet all of these conditions:
+ .
+ a) The work must carry prominent notices stating that you modified
+ it, and giving a relevant date.
+ .
+ b) The work must carry prominent notices stating that it is
+ released under this License and any conditions added under section
+ 7. This requirement modifies the requirement in section 4 to
+ "keep intact all notices".
+ .
+ c) You must license the entire work, as a whole, under this
+ License to anyone who comes into possession of a copy. This
+ License will therefore apply, along with any applicable section 7
+ additional terms, to the whole of the work, and all its parts,
+ regardless of how they are packaged. This License gives no
+ permission to license the work in any other way, but it does not
+ invalidate such permission if you have separately received it.
+ .
+ d) If the work has interactive user interfaces, each must display
+ Appropriate Legal Notices; however, if the Program has interactive
+ interfaces that do not display Appropriate Legal Notices, your
+ work need not make them do so.
+ .
+ A compilation of a covered work with other separate and independent
+ works, which are not by their nature extensions of the covered work,
+ and which are not combined with it such as to form a larger program,
+ in or on a volume of a storage or distribution medium, is called an
+ "aggregate" if the compilation and its resulting copyright are not
+ used to limit the access or legal rights of the compilation's users
+ beyond what the individual works permit. Inclusion of a covered work
+ in an aggregate does not cause this License to apply to the other
+ parts of the aggregate.
+ .
+ 6. Conveying Non-Source Forms.
+ .
+ You may convey a covered work in object code form under the terms
+ of sections 4 and 5, provided that you also convey the
+ machine-readable Corresponding Source under the terms of this License,
+ in one of these ways:
+ .
+ a) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by the
+ Corresponding Source fixed on a durable physical medium
+ customarily used for software interchange.
+ .
+ b) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by a
+ written offer, valid for at least three years and valid for as
+ long as you offer spare parts or customer support for that product
+ model, to give anyone who possesses the object code either (1) a
+ copy of the Corresponding Source for all the software in the
+ product that is covered by this License, on a durable physical
+ medium customarily used for software interchange, for a price no
+ more than your reasonable cost of physically performing this
+ conveying of source, or (2) access to copy the
+ Corresponding Source from a network server at no charge.
+ .
+ c) Convey individual copies of the object code with a copy of the
+ written offer to provide the Corresponding Source. This
+ alternative is allowed only occasionally and noncommercially, and
+ only if you received the object code with such an offer, in accord
+ with subsection 6b.
+ .
+ d) Convey the object code by offering access from a designated
+ place (gratis or for a charge), and offer equivalent access to the
+ Corresponding Source in the same way through the same place at no
+ further charge. You need not require recipients to copy the
+ Corresponding Source along with the object code. If the place to
+ copy the object code is a network server, the Corresponding Source
+ may be on a different server (operated by you or a third party)
+ that supports equivalent copying facilities, provided you maintain
+ clear directions next to the object code saying where to find the
+ Corresponding Source. Regardless of what server hosts the
+ Corresponding Source, you remain obligated to ensure that it is
+ available for as long as needed to satisfy these requirements.
+ .
+ e) Convey the object code using peer-to-peer transmission, provided
+ you inform other peers where the object code and Corresponding
+ Source of the work are being offered to the general public at no
+ charge under subsection 6d.
+ .
+ A separable portion of the object code, whose source code is excluded
+ from the Corresponding Source as a System Library, need not be
+ included in conveying the object code work.
+ .
+ A "User Product" is either (1) a "consumer product", which means any
+ tangible personal property which is normally used for personal, family,
+ or household purposes, or (2) anything designed or sold for incorporation
+ into a dwelling. In determining whether a product is a consumer product,
+ doubtful cases shall be resolved in favor of coverage. For a particular
+ product received by a particular user, "normally used" refers to a
+ typical or common use of that class of product, regardless of the status
+ of the particular user or of the way in which the particular user
+ actually uses, or expects or is expected to use, the product. A product
+ is a consumer product regardless of whether the product has substantial
+ commercial, industrial or non-consumer uses, unless such uses represent
+ the only significant mode of use of the product.
+ .
+ "Installation Information" for a User Product means any methods,
+ procedures, authorization keys, or other information required to install
+ and execute modified versions of a covered work in that User Product from
+ a modified version of its Corresponding Source. The information must
+ suffice to ensure that the continued functioning of the modified object
+ code is in no case prevented or interfered with solely because
+ modification has been made.
+ .
+ If you convey an object code work under this section in, or with, or
+ specifically for use in, a User Product, and the conveying occurs as
+ part of a transaction in which the right of possession and use of the
+ User Product is transferred to the recipient in perpetuity or for a
+ fixed term (regardless of how the transaction is characterized), the
+ Corresponding Source conveyed under this section must be accompanied
+ by the Installation Information. But this requirement does not apply
+ if neither you nor any third party retains the ability to install
+ modified object code on the User Product (for example, the work has
+ been installed in ROM).
+ .
+ The requirement to provide Installation Information does not include a
+ requirement to continue to provide support service, warranty, or updates
+ for a work that has been modified or installed by the recipient, or for
+ the User Product in which it has been modified or installed. Access to a
+ network may be denied when the modification itself materially and
+ adversely affects the operation of the network or violates the rules and
+ protocols for communication across the network.
+ .
+ Corresponding Source conveyed, and Installation Information provided,
+ in accord with this section must be in a format that is publicly
+ documented (and with an implementation available to the public in
+ source code form), and must require no special password or key for
+ unpacking, reading or copying.
+ .
+ 7. Additional Terms.
+ .
+ "Additional permissions" are terms that supplement the terms of this
+ License by making exceptions from one or more of its conditions.
+ Additional permissions that are applicable to the entire Program shall
+ be treated as though they were included in this License, to the extent
+ that they are valid under applicable law. If additional permissions
+ apply only to part of the Program, that part may be used separately
+ under those permissions, but the entire Program remains governed by
+ this License without regard to the additional permissions.
+ .
+ When you convey a copy of a covered work, you may at your option
+ remove any additional permissions from that copy, or from any part of
+ it. (Additional permissions may be written to require their own
+ removal in certain cases when you modify the work.) You may place
+ additional permissions on material, added by you to a covered work,
+ for which you have or can give appropriate copyright permission.
+ .
+ Notwithstanding any other provision of this License, for material you
+ add to a covered work, you may (if authorized by the copyright holders of
+ that material) supplement the terms of this License with terms:
+ .
+ a) Disclaiming warranty or limiting liability differently from the
+ terms of sections 15 and 16 of this License; or
+ .
+ b) Requiring preservation of specified reasonable legal notices or
+ author attributions in that material or in the Appropriate Legal
+ Notices displayed by works containing it; or
+ .
+ c) Prohibiting misrepresentation of the origin of that material, or
+ requiring that modified versions of such material be marked in
+ reasonable ways as different from the original version; or
+ .
+ d) Limiting the use for publicity purposes of names of licensors or
+ authors of the material; or
+ .
+ e) Declining to grant rights under trademark law for use of some
+ trade names, trademarks, or service marks; or
+ .
+ f) Requiring indemnification of licensors and authors of that
+ material by anyone who conveys the material (or modified versions of
+ it) with contractual assumptions of liability to the recipient, for
+ any liability that these contractual assumptions directly impose on
+ those licensors and authors.
+ .
+ All other non-permissive additional terms are considered "further
+ restrictions" within the meaning of section 10. If the Program as you
+ received it, or any part of it, contains a notice stating that it is
+ governed by this License along with a term that is a further
+ restriction, you may remove that term. If a license document contains
+ a further restriction but permits relicensing or conveying under this
+ License, you may add to a covered work material governed by the terms
+ of that license document, provided that the further restriction does
+ not survive such relicensing or conveying.
+ .
+ If you add terms to a covered work in accord with this section, you
+ must place, in the relevant source files, a statement of the
+ additional terms that apply to those files, or a notice indicating
+ where to find the applicable terms.
+ .
+ Additional terms, permissive or non-permissive, may be stated in the
+ form of a separately written license, or stated as exceptions;
+ the above requirements apply either way.
+ .
+ 8. Termination.
+ .
+ You may not propagate or modify a covered work except as expressly
+ provided under this License. Any attempt otherwise to propagate or
+ modify it is void, and will automatically terminate your rights under
+ this License (including any patent licenses granted under the third
+ paragraph of section 11).
+ .
+ However, if you cease all violation of this License, then your
+ license from a particular copyright holder is reinstated (a)
+ provisionally, unless and until the copyright holder explicitly and
+ finally terminates your license, and (b) permanently, if the copyright
+ holder fails to notify you of the violation by some reasonable means
+ prior to 60 days after the cessation.
+ .
+ Moreover, your license from a particular copyright holder is
+ reinstated permanently if the copyright holder notifies you of the
+ violation by some reasonable means, this is the first time you have
+ received notice of violation of this License (for any work) from that
+ copyright holder, and you cure the violation prior to 30 days after
+ your receipt of the notice.
+ .
+ Termination of your rights under this section does not terminate the
+ licenses of parties who have received copies or rights from you under
+ this License. If your rights have been terminated and not permanently
+ reinstated, you do not qualify to receive new licenses for the same
+ material under section 10.
+ .
+ 9. Acceptance Not Required for Having Copies.
+ .
+ You are not required to accept this License in order to receive or
+ run a copy of the Program. Ancillary propagation of a covered work
+ occurring solely as a consequence of using peer-to-peer transmission
+ to receive a copy likewise does not require acceptance. However,
+ nothing other than this License grants you permission to propagate or
+ modify any covered work. These actions infringe copyright if you do
+ not accept this License. Therefore, by modifying or propagating a
+ covered work, you indicate your acceptance of this License to do so.
+ .
+ 10. Automatic Licensing of Downstream Recipients.
+ .
+ Each time you convey a covered work, the recipient automatically
+ receives a license from the original licensors, to run, modify and
+ propagate that work, subject to this License. You are not responsible
+ for enforcing compliance by third parties with this License.
+ .
+ An "entity transaction" is a transaction transferring control of an
+ organization, or substantially all assets of one, or subdividing an
+ organization, or merging organizations. If propagation of a covered
+ work results from an entity transaction, each party to that
+ transaction who receives a copy of the work also receives whatever
+ licenses to the work the party's predecessor in interest had or could
+ give under the previous paragraph, plus a right to possession of the
+ Corresponding Source of the work from the predecessor in interest, if
+ the predecessor has it or can get it with reasonable efforts.
+ .
+ You may not impose any further restrictions on the exercise of the
+ rights granted or affirmed under this License. For example, you may
+ not impose a license fee, royalty, or other charge for exercise of
+ rights granted under this License, and you may not initiate litigation
+ (including a cross-claim or counterclaim in a lawsuit) alleging that
+ any patent claim is infringed by making, using, selling, offering for
+ sale, or importing the Program or any portion of it.
+ .
+ 11. Patents.
+ .
+ A "contributor" is a copyright holder who authorizes use under this
+ License of the Program or a work on which the Program is based. The
+ work thus licensed is called the contributor's "contributor version".
+ .
+ A contributor's "essential patent claims" are all patent claims
+ owned or controlled by the contributor, whether already acquired or
+ hereafter acquired, that would be infringed by some manner, permitted
+ by this License, of making, using, or selling its contributor version,
+ but do not include claims that would be infringed only as a
+ consequence of further modification of the contributor version. For
+ purposes of this definition, "control" includes the right to grant
+ patent sublicenses in a manner consistent with the requirements of
+ this License.
+ .
+ Each contributor grants you a non-exclusive, worldwide, royalty-free
+ patent license under the contributor's essential patent claims, to
+ make, use, sell, offer for sale, import and otherwise run, modify and
+ propagate the contents of its contributor version.
+ .
+ In the following three paragraphs, a "patent license" is any express
+ agreement or commitment, however denominated, not to enforce a patent
+ (such as an express permission to practice a patent or covenant not to
+ sue for patent infringement). To "grant" such a patent license to a
+ party means to make such an agreement or commitment not to enforce a
+ patent against the party.
+ .
+ If you convey a covered work, knowingly relying on a patent license,
+ and the Corresponding Source of the work is not available for anyone
+ to copy, free of charge and under the terms of this License, through a
+ publicly available network server or other readily accessible means,
+ then you must either (1) cause the Corresponding Source to be so
+ available, or (2) arrange to deprive yourself of the benefit of the
+ patent license for this particular work, or (3) arrange, in a manner
+ consistent with the requirements of this License, to extend the patent
+ license to downstream recipients. "Knowingly relying" means you have
+ actual knowledge that, but for the patent license, your conveying the
+ covered work in a country, or your recipient's use of the covered work
+ in a country, would infringe one or more identifiable patents in that
+ country that you have reason to believe are valid.
+ .
+ If, pursuant to or in connection with a single transaction or
+ arrangement, you convey, or propagate by procuring conveyance of, a
+ covered work, and grant a patent license to some of the parties
+ receiving the covered work authorizing them to use, propagate, modify
+ or convey a specific copy of the covered work, then the patent license
+ you grant is automatically extended to all recipients of the covered
+ work and works based on it.
+ .
+ A patent license is "discriminatory" if it does not include within
+ the scope of its coverage, prohibits the exercise of, or is
+ conditioned on the non-exercise of one or more of the rights that are
+ specifically granted under this License. You may not convey a covered
+ work if you are a party to an arrangement with a third party that is
+ in the business of distributing software, under which you make payment
+ to the third party based on the extent of your activity of conveying
+ the work, and under which the third party grants, to any of the
+ parties who would receive the covered work from you, a discriminatory
+ patent license (a) in connection with copies of the covered work
+ conveyed by you (or copies made from those copies), or (b) primarily
+ for and in connection with specific products or compilations that
+ contain the covered work, unless you entered into that arrangement,
+ or that patent license was granted, prior to 28 March 2007.
+ .
+ Nothing in this License shall be construed as excluding or limiting
+ any implied license or other defenses to infringement that may
+ otherwise be available to you under applicable patent law.
+ .
+ 12. No Surrender of Others' Freedom.
+ .
+ If conditions are imposed on you (whether by court order, agreement or
+ otherwise) that contradict the conditions of this License, they do not
+ excuse you from the conditions of this License. If you cannot convey a
+ covered work so as to satisfy simultaneously your obligations under this
+ License and any other pertinent obligations, then as a consequence you may
+ not convey it at all. For example, if you agree to terms that obligate you
+ to collect a royalty for further conveying from those to whom you convey
+ the Program, the only way you could satisfy both those terms and this
+ License would be to refrain entirely from conveying the Program.
+ .
+ 13. Remote Network Interaction; Use with the GNU General Public License.
+ .
+ Notwithstanding any other provision of this License, if you modify the
+ Program, your modified version must prominently offer all users
+ interacting with it remotely through a computer network (if your version
+ supports such interaction) an opportunity to receive the Corresponding
+ Source of your version by providing access to the Corresponding Source
+ from a network server at no charge, through some standard or customary
+ means of facilitating copying of software. This Corresponding Source
+ shall include the Corresponding Source for any work covered by version 3
+ of the GNU General Public License that is incorporated pursuant to the
+ following paragraph.
+ .
+ Notwithstanding any other provision of this License, you have
+ permission to link or combine any covered work with a work licensed
+ under version 3 of the GNU General Public License into a single
+ combined work, and to convey the resulting work. The terms of this
+ License will continue to apply to the part which is the covered work,
+ but the work with which it is combined will remain governed by version
+ 3 of the GNU General Public License.
+ .
+ 14. Revised Versions of this License.
+ .
+ The Free Software Foundation may publish revised and/or new versions of
+ the GNU Affero General Public License from time to time. Such new versions
+ will be similar in spirit to the present version, but may differ in detail to
+ address new problems or concerns.
+ .
+ Each version is given a distinguishing version number. If the
+ Program specifies that a certain numbered version of the GNU Affero General
+ Public License "or any later version" applies to it, you have the
+ option of following the terms and conditions either of that numbered
+ version or of any later version published by the Free Software
+ Foundation. If the Program does not specify a version number of the
+ GNU Affero General Public License, you may choose any version ever published
+ by the Free Software Foundation.
+ .
+ If the Program specifies that a proxy can decide which future
+ versions of the GNU Affero General Public License can be used, that proxy's
+ public statement of acceptance of a version permanently authorizes you
+ to choose that version for the Program.
+ .
+ Later license versions may give you additional or different
+ permissions. However, no additional obligations are imposed on any
+ author or copyright holder as a result of your choosing to follow a
+ later version.
+ .
+ 15. Disclaimer of Warranty.
+ .
+ THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+ APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+ HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+ OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+ THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+ IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+ .
+ 16. Limitation of Liability.
+ .
+ IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+ WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+ THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+ GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+ USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+ DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+ PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+ EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+ SUCH DAMAGES.
+ .
+ 17. Interpretation of Sections 15 and 16.
+ .
+ If the disclaimer of warranty and limitation of liability provided
+ above cannot be given local legal effect according to their terms,
+ reviewing courts shall apply local law that most closely approximates
+ an absolute waiver of all civil liability in connection with the
+ Program, unless a warranty or assumption of liability accompanies a
+ copy of the Program in return for a fee.
+ .
+ END OF TERMS AND CONDITIONS
+ .
+ How to Apply These Terms to Your New Programs
+ .
+ If you develop a new program, and you want it to be of the greatest
+ possible use to the public, the best way to achieve this is to make it
+ free software which everyone can redistribute and change under these terms.
+ .
+ To do so, attach the following notices to the program. It is safest
+ to attach them to the start of each source file to most effectively
+ state the exclusion of warranty; and each file should have at least
+ the "copyright" line and a pointer to where the full notice is found.
+ .
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+ .
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+ .
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+ .
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ .
+ Also add information on how to contact you by electronic and paper mail.
+ .
+ If your software can interact with users remotely through a computer
+ network, you should also make sure that it provides a way for users to
+ get its source. For example, if your program is a web application, its
+ interface could display a "Source" link that leads users to an archive
+ of the code. There are many ways you could offer source, and different
+ solutions will be better for different programs; see section 13 for the
+ specific requirements.
+ .
+ You should also get your employer (if you work as a programmer) or school,
+ if any, to sign a "copyright disclaimer" for the program, if necessary.
+ For more information on this, and how to apply and follow the GNU AGPL, see
+ <http://www.gnu.org/licenses/>.
+
+License: BSD-2
+ 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 THE COPYRIGHT
+ OWNER OR CONTRIBUTORS 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.
+
+License: BSD-3
+ All rights reserved.
+ .
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are met:
+ .
+ 1. Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+ .
+ 2. 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.
+ .
+ 3. Neither the name of the copyright holder nor the names of its contributors
+ may be used to endorse or promote products derived from this software
+ without specific prior written permission.
+ .
+ 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 THE COPYRIGHT HOLDER OR CONTRIBUTORS 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.
+
+License: Expat
+ Permission is hereby granted, free of charge, to any person obtaining a
+ copy of this software and associated documentation files (the "Software"),
+ to deal in the Software without restriction, including without limitation
+ the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ and/or sell copies of the Software, and to permit persons to whom the
+ Software is furnished to do so, subject to the following conditions:
+ .
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+
+License: GFDL-1.3+
+ Permission is granted to copy, distribute and/or modify this document
+ under the terms of the GNU Free Documentation License, Version 1.3
+ or any later version published by the Free Software Foundation;
+ with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts.
+ .
+ On Debian systems, the complete text of the GNU Free Documentation License
+ version 1.3 can be found in "/usr/share/common-licenses/GFDL-1.3".
+
+License: GPL-2+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 2 of the License, or
+ (at your option) any later version.
+ .
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+ .
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ .
+ The complete text of the GNU General Public License
+ can be found in /usr/share/common-licenses/GPL-2 file.
+
+License: GPL-3+
+ This package is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+ .
+ This package is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+ .
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>
+ .
+ On Debian systems, the complete text of the GNU General
+ Public License version 3 can be found in "/usr/share/common-licenses/GPL-3".
+
+License: LGPL-2+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+ .
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+ .
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ .
+ On Debian systems, the complete text of the GNU Library General Public License
+ version 2 can be found in "/usr/share/common-licenses/LGPL-2".
+
+License: MIT
+ All rights reserved. No part of this source code may be reproduced,
+ stored in a retrieval system, or transmitted, in any form or by any
+ means, electronic, mechanical, photocopying, recording or otherwise,
+ except as stated in the end-user licence agreement, without the prior
+ permission of the copyright owners.
+ .
+ Permission to use, copy, modify, and distribute this software and its
+ documentation for any purpose and without fee is hereby granted, provided
+ that the above copyright notice appear in all copies and that both that
+ copyright notice and this permission notice appear in supporting
+ documentation, and that the name of OSF, UI or X/Open not be used in
+ advertising or publicity pertaining to distribution of the software
+ without specific, written prior permission. OSF, UI and X/Open make
+ no representations about the suitability of this software for any purpose.
+ It is provided "as is" without express or implied warranty.
+ .
+ OSF, UI and X/Open DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
+ EVENT SHALL OSF, UI or X/Open BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+ CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
+ USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
+ OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ PERFORMANCE OF THIS SOFTWARE.
diff --git a/debian/icc-utils.postinst b/debian/icc-utils.postinst
new file mode 100644
index 0000000..42dfee1
--- /dev/null
+++ b/debian/icc-utils.postinst
@@ -0,0 +1,41 @@
+#!/bin/sh
+#
+# see: dh_installdeb(1)
+
+set -e
+
+# summary of how this script can be called:
+# * <postinst> `configure' <most-recently-configured-version>
+# * <old-postinst> `abort-upgrade' <new version>
+# * <conflictor's-postinst> `abort-remove' `in-favour' <package>
+# <new-version>
+# * <postinst> `abort-remove'
+# * <deconfigured's-postinst> `abort-deconfigure' `in-favour'
+# <failed-install-package> <version> `removing'
+# <conflicting-package> <version>
+# for details, see http://www.debian.org/doc/debian-policy/ or
+# the debian-policy package
+
+# source debconf library
+#. /usr/share/debconf/confmodule
+
+
+case "$1" in
+
+ configure|abort-upgrade|abort-remove|abort-deconfigure)
+ # Replace documentation directory symlink
+ dpkg-maintscript-helper symlink_to_dir /usr/share/doc/icc-utils /usr/share/doc/argyll-doc 1.6.3-2~ -- "$@"
+
+ ;;
+
+ *)
+ echo "postinst called with unknown argument \`$1'" >&2
+ exit 1
+ ;;
+
+esac
+
+#DEBHELPER#
+
+
+exit 0
diff --git a/debian/icc-utils.postrm b/debian/icc-utils.postrm
new file mode 100644
index 0000000..eb12775
--- /dev/null
+++ b/debian/icc-utils.postrm
@@ -0,0 +1,33 @@
+#! /bin/sh
+# postrm script for icc-utils
+#
+# see: dh_installdeb(1)
+
+set -e
+
+# summary of how this script can be called:
+# * <postrm> `remove'
+# * <postrm> `purge'
+# * <old-postrm> `upgrade' <new-version>
+# * <new-postrm> `failed-upgrade' <old-version>
+# * <new-postrm> `abort-install'
+# * <new-postrm> `abort-install' <old-version>
+# * <new-postrm> `abort-upgrade' <old-version>
+# * <disappearer's-postrm> `disappear' <r>overwrit>r> <new-version>
+# for details, see /usr/share/doc/packaging-manual/
+
+case "$1" in
+ purge|remove|upgrade|failed-upgrade|abort-install|abort-upgrade|disappear)
+ dpkg-maintscript-helper symlink_to_dir /usr/share/doc/icc-utils /usr/share/doc/argyll-doc 1.6.3-2~ -- "$@"
+ ;;
+
+ *)
+ echo "postrm called with unknown argument \`$1'" >&2
+ exit 0
+
+esac
+
+# dh_installdeb will replace this with shell code automatically
+# generated by other debhelper scripts.
+
+#DEBHELPER#
diff --git a/debian/icc-utils.preinst b/debian/icc-utils.preinst
new file mode 100644
index 0000000..98e4723
--- /dev/null
+++ b/debian/icc-utils.preinst
@@ -0,0 +1,32 @@
+#!/bin/sh
+# preinst script for icc-utils
+#
+# see: dh_installdeb(1)
+
+set -e
+
+# summary of how this script can be called:
+# * <new-preinst> `install'
+# * <new-preinst> `install' <old-version>
+# * <new-preinst> `upgrade' <old-version>
+# * <old-preinst> `abort-upgrade' <new-version>
+# for details, see http://www.debian.org/doc/debian-policy/ or
+# the debian-policy package
+
+
+case "$1" in
+ install|upgrade|abort-upgrade)
+ dpkg-maintscript-helper symlink_to_dir /usr/share/doc/icc-utils /usr/share/doc/argyll-doc 1.6.3-2~ -- "$@"
+ ;;
+ *)
+ echo "preinst called with unknown argument \`$1'" >&2
+ exit 1
+ ;;
+esac
+
+# dh_installdeb will replace this with shell code automatically
+# generated by other debhelper scripts.
+
+#DEBHELPER#
+
+exit 0
diff --git a/debian/man/applycal.1 b/debian/man/applycal.1
new file mode 100644
index 0000000..eb2f902
--- /dev/null
+++ b/debian/man/applycal.1
@@ -0,0 +1,30 @@
+.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.44.1.
+.TH APPLY "1" "September 2014" "applycal" "User Commands"
+.SH NAME
+Apply \- Apply device calibration to an ICC profile.
+.SH DESCRIPTION
+Apply device calibration to an ICC profile
+.SH SYNOPSIS
+.B applycal
+.RB [\-options] [calfile.cal] inprof.icc [outprof.icc]
+.TP
+\fB\-v\fR
+Verbose mode
+.TP
+\fB\-a\fR
+Apply or re\-apply calibration (default)
+.TP
+\fB\-u\fR
+Remove calibration
+.TP
+\fB\-c\fR
+Check calibration
+.TP
+calfile.cal
+Calibration file to apply
+.TP
+inprof.icc
+ICC profile to read
+.TP
+outprof.icc
+modified ICC profile to write \ No newline at end of file
diff --git a/debian/man/average.1 b/debian/man/average.1
new file mode 100644
index 0000000..ae80f1f
--- /dev/null
+++ b/debian/man/average.1
@@ -0,0 +1,29 @@
+.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.44.1.
+.TH AVERAGE "1" "September 2014" "average" "User Commands"
+.SH NAME
+Average \- Average or merge values in .ti3 like files.
+.SH DESCRIPTION
+Average or merge values in .ti3 like files
+.IP
+Diagnostic: Too few arguments (1, minimum is 2)
+.SH SYNOPSIS
+.B average
+.RB [\-options]\ \input1.ti3\ \input2.ti3 ... output.ti3
+.TP
+\fB\-v\fR
+Verbose
+.TP
+\fB\-m\fR
+Merge rather than average
+.TP
+input1.ti3
+First input file
+.TP
+input2.ti3
+Second input file
+.TP
+\&...
+etc.
+.TP
+output.ti3
+Resulting averaged or merged output file
diff --git a/debian/man/cb2ti3.1 b/debian/man/cb2ti3.1
new file mode 100644
index 0000000..ddf3a9d
--- /dev/null
+++ b/debian/man/cb2ti3.1
@@ -0,0 +1,21 @@
+.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.44.1.
+.TH CONVERT "1" "September 2014" "cb2ti3" "User Commands"
+.SH NAME
+Convert \- Convert Colorblind raw device profile data to Argyll data.
+.SH DESCRIPTION
+Convert Colorblind raw device profile data to Argyll data
+.SH SYNOPSIS
+.B cb2ti3
+.RB [\-v] [\-l limit] infile outfile
+.TP
+\fB\-v\fR
+Verbose mode
+.TP
+\fB\-l\fR limit
+Set inklimit in .ti3 file
+.TP
+infile
+Base name for input.CMY and input.nCIE file
+.TP
+outfile
+Base name for output.ti3 file \ No newline at end of file
diff --git a/debian/man/cctiff.1 b/debian/man/cctiff.1
new file mode 100644
index 0000000..cb23e31
--- /dev/null
+++ b/debian/man/cctiff.1
@@ -0,0 +1,73 @@
+.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.44.1.
+.TH COLOR "1" "September 2014" "cctiff" "User Commands"
+.SH NAME
+Color \- Color Correct a TIFF file using any sequence of ICC profiles or Calibrations.
+.SH DESCRIPTION
+Color Correct a TIFF or JPEG file using any sequence of ICC profiles or Calibrations
+.SH SYNOPSIS
+.B cctiff
+.RB [\-options]\ { [\-i intent]\ profile.icc\ |\ calbrtn.cal\ ...}\ infile.(tif|jpg)\ outfile.(tif|jpg)
+.TP
+\fB\-v\fR
+Verbose.
+.TP
+\fB\-c\fR
+Combine linearisation curves into one transform.
+.TP
+\fB\-p\fR
+Use slow precise correction.
+.TP
+\fB\-k\fR
+Check fast result against precise, and report.
+.TP
+\fB\-r\fR n
+Override the default CLUT resolution
+.TP
+\fB\-t\fR n
+Choose output encoding from 1..n
+.TP
+\fB\-f\fR [T|J]
+Set output format to Tiff or Jpeg (Default is same as input)
+.TP
+\fB\-q\fR quality
+Set JPEG quality 1..100 (Default 80)
+.TP
+\fB\-a\fR
+Read and Write planes > 4 as alpha planes
+.TP
+\fB\-I\fR
+Ignore any file or profile colorspace mismatches
+.TP
+\fB\-D\fR
+Don't append or set the output TIFF or JPEG description
+.TP
+\fB\-e\fR profile.[icc | tiff | jpg]
+Optionally embed a profile in the destination TIFF or JPEG file.
+.IP
+Then for each profile in sequence:
+.TP
+\fB\-i\fR intent
+p = perceptual, r = relative colorimetric,
+s = saturation, a = absolute colorimetric
+.TP
+\fB\-o\fR order
+n = normal (priority: lut > matrix > monochrome)
+r = reverse (priority: monochrome > matrix > lut)
+.TP
+profile.[icc | tiff]
+Device, Link or Abstract profile
+.IP
+( May be embedded profile in TIFF/JPEG file)
+.IP
+or each calibration file in sequence:
+.TP
+\fB\-d\fR dir
+f = forward cal. (default), b = backwards cal.
+.TP
+calbrtn.cal
+Device calibration file.
+.TP
+infile.tif/jpg
+Input TIFF/JPEG file in appropriate color space
+.IP
+outfile.tif/jpg Output TIFF/JPEG file
diff --git a/debian/man/ccxxmake.1 b/debian/man/ccxxmake.1
new file mode 100644
index 0000000..337db92
--- /dev/null
+++ b/debian/man/ccxxmake.1
@@ -0,0 +1,91 @@
+.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.44.1.
+.TH CREATE "1" "September 2014" "ccmxmake" "User Commands"
+.SH NAME
+Create \- Create CCMX or CCSS.
+.SH DESCRIPTION
+Create CCMX or CCSS
+.SH SYNOPSIS
+.B ccmxmake
+.RB [\-options]\ output.ccmx
+.TP
+\fB\-v\fR
+Verbose mode
+.TP
+\fB\-S\fR
+Create CCSS rather than CCMX
+.HP
+\fB\-f\fR file1.ti3[,file2.ti3] Create from one or two .ti3 files rather than measure.
+.TP
+\fB\-display\fR displayname
+Choose X11 display name
+.TP
+\fB\-d\fR n[,m]
+Choose the display n from the following list (default 1)
+Optionally choose different display m for VideoLUT access
+.IP
+1 name = ':0.0'
+1 = 'Screen 1, Output DVI\-I\-1 at 0, 0, width 1920, height 1080'
+.TP
+\fB\-dweb[\fR:port]
+Display via a web server at port (default 8080)
+.TP
+\fB\-p\fR
+Use telephoto mode (ie. for a projector) (if available)
+.TP
+\fB\-P\fR ho,vo,ss[,vs]
+Position test window and scale it
+ho,vi: 0.0 = left/top, 0.5 = center, 1.0 = right/bottom etc.
+ss: 0.5 = half, 1.0 = normal, 2.0 = double etc.
+.TP
+\fB\-F\fR
+Fill whole screen with black background
+.TP
+\fB\-n\fR
+Don't set override redirect on test window
+.TP
+\fB\-N\fR
+Disable initial calibration of instrument if possible
+.TP
+\fB\-H\fR
+Use high resolution spectrum mode (if available)
+.TP
+\fB\-C\fR "command"
+Invoke shell "command" each time a color is set
+.TP
+\fB\-o\fR observ
+Choose CIE Observer for CCMX spectrometer data:
+1931_2 (def), 1964_10, S&B 1955_2, shaw, J&V 1978_2
+.TP
+\fB\-s\fR steps
+Override default patch sequence combination steps (default 1)
+.TP
+\fB\-W\fR n|h|x
+Override serial port flow control: n = none, h = HW, x = Xon/Xoff
+.TP
+\fB\-D\fR [level]
+Print debug diagnostics to stderr
+.TP
+\fB\-E\fR desciption
+Override the default overall description
+.TP
+\fB\-I\fR displayname
+Set display make and model description
+.TP
+\fB\-T\fR displaytech
+Set display technology description (ie. CRT, LCD etc.)
+.TP
+\fB\-U\fR c
+Set UI selection character(s)
+.TP
+\fB\-Y\fR r|n
+Set or override refresh/non\-refresh display type
+.TP
+\fB\-Y\fR R:rate
+Override measured refresh rate with rate Hz
+.TP
+\fB\-Y\fR A
+Use non\-adaptive integration time mode (if available).
+.IP
+correction.ccmx | calibration.ccss
+.IP
+File to save result to
diff --git a/debian/man/chartread.1 b/debian/man/chartread.1
new file mode 100644
index 0000000..1d8285e
--- /dev/null
+++ b/debian/man/chartread.1
@@ -0,0 +1,96 @@
+.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.44.1.
+.TH READ "1" "September 2014" "chartread" "User Commands"
+.SH NAME
+Read \- Read Target Test Chart.
+.SH DESCRIPTION
+Read Target Test Chart
+.SH SYNOPSIS
+.B chartread
+.RB [\-options]\ outfile
+.TP
+\fB\-v\fR
+Verbose mode
+.TP
+\fB\-c\fR listno
+Set communication port from the following list (default 1)
+.IP
+** No ports found **
+.TP
+\fB\-t\fR
+Use transmission measurement mode
+.TP
+\fB\-d\fR
+Use display measurement mode (white Y relative results)
+.TP
+\fB\-e\fR
+Emissive for transparency on a light box
+.TP
+\fB\-p\fR
+Measure patch by patch rather than strip
+.TP
+\fB\-x\fR [lx]
+Take external values, either L*a*b* (\fB\-xl\fR) or XYZ (\fB\-xx\fR).
+.TP
+\fB\-n\fR
+Don't save spectral information (default saves spectral)
+.TP
+\fB\-l\fR
+Save CIE as D50 L*a*b* rather than XYZ
+.TP
+\fB\-L\fR
+Save CIE as D50 L*a*b* as well as XYZ
+.TP
+\fB\-r\fR
+Resume reading partly read chart
+.TP
+\fB\-I\fR file.cal
+Override calibration info from .ti2 in resulting .ti3
+.TP
+\fB\-F\fR filter
+Set filter configuration (if aplicable):
+.TP
+n
+None
+.TP
+p
+Polarising filter
+.TP
+6
+D65
+.TP
+u
+U.V. Cut
+.TP
+\fB\-N\fR
+Disable initial calibration of instrument if possible
+.TP
+\fB\-B\fR
+Disable auto bi\-directional strip recognition
+.TP
+\fB\-H\fR
+Use high resolution spectrum mode (if available)
+.TP
+\fB\-X\fR file.ccmx
+Apply Colorimeter Correction Matrix
+.TP
+\fB\-X\fR file.ccss
+Use Colorimeter Calibration Spectral Samples for calibration
+.TP
+\fB\-Q\fR observ
+Choose CIE Observer for CCSS instrument:
+1931_2 (def), 1964_10, S&B 1955_2, shaw, J&V 1978_2
+.TP
+\fB\-T\fR ratio
+Modify strip patch consistency tolerance by ratio
+.TP
+\fB\-S\fR
+Suppress wrong strip & unexpected value warnings
+.TP
+\fB\-W\fR n|h|x
+Override serial port flow control: n = none, h = HW, x = Xon/Xoff
+.TP
+\fB\-D\fR [level]
+Print debug diagnostics to stderr
+.TP
+outfile
+Base name for input[ti2]/output[ti3] file
diff --git a/debian/man/collink.1 b/debian/man/collink.1
new file mode 100644
index 0000000..35d6c02
--- /dev/null
+++ b/debian/man/collink.1
@@ -0,0 +1,241 @@
+.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.44.1.
+.TH LINK "1" "September 2014" "collink" "User Commands"
+.SH NAME
+Link \- Link ICC profiles.
+.SH DESCRIPTION
+Link ICC profiles
+.SH SYNOPSIS
+.B collink
+.RB [options]\ srcprofile\ dstprofile\ linkedprofile
+.TP
+\fB\-v\fR Verbose
+.HP
+\fB\-A\fR manufacturer Manufacturer description string
+.TP
+\fB\-M\fR model
+Model description string
+.TP
+\fB\-D\fR description
+Profile Description string (Default "inoutfile")
+.TP
+\fB\-C\fR copyright
+Copyright string
+.TP
+\fB\-V\fR
+Verify existing profile, rather than link
+.TP
+\fB\-q\fR lmhu
+Quality \- Low, Medium (def), High, Ultra
+.TP
+\fB\-r\fR res
+Override clut res. set by \fB\-q\fR
+.TP
+\fB\-n\fR
+Don't preserve device linearization curves in result
+.TP
+\fB\-f\fR
+Special :\- Force neutral colors to be K only output
+.TP
+\fB\-fk\fR
+Special :\- Force K only neutral colors to be K only output
+.TP
+\fB\-F\fR
+Special :\- Force all colors to be K only output
+.TP
+\fB\-fcmy\fR
+Special :\- Force 100% C,M or Y only to stay pure
+.TP
+\fB\-p\fR absprof
+Include abstract profile in link
+.TP
+\fB\-a\fR file.cal
+Apply calibration curves to link output and append linear
+.TP
+\fB\-H\fR file.cal
+Append calibration curves to 3dlut
+.TP
+\fB\-s\fR
+Simple Mode (default)
+.TP
+\fB\-g\fR [src.gam]
+Gamut Mapping Mode [optional source image gamut]
+.TP
+\fB\-G\fR [src.gam]
+Gamut Mapping Mode using inverse outprofile A2B
+.IP
+Simple Mode Options:
+\fB\-i\fR in_intent p = perceptual, r = relative colorimetric,
+.IP
+s = saturation, a = absolute colorimetric
+.TP
+\fB\-o\fR out_intent
+p = perceptual, r = relative colorimetric,
+s = saturation, a = absolute colorimetric
+.TP
+Gamut Mapping
+Mode Options:
+.TP
+\fB\-i\fR intent
+set linking intent from the following choice:
+.IP
+a \- Absolute Colorimetric (in Jab) [ICC Absolute Colorimetric]
+.IP
+aw \- Absolute Colorimetric (in Jab) with scaling to fit white point
+aa \- Absolute Appearance
+.IP
+r \- White Point Matched Appearance [ICC Relative Colorimetric]
+.IP
+la \- Luminance axis matched Appearance
+.IP
+p \- Perceptual (Preferred) (Default) [ICC Perceptual]
+.IP
+pa \- Perceptual Apperance
+ms \- Saturation
+.IP
+s \- Enhanced Saturation [ICC Saturation]
+.IP
+al \- Absolute Colorimetric (Lab)
+rl \- White Point Matched Colorimetric (Lab)
+.TP
+\fB\-w\fR [J,a,b]
+Use forced whitepoint hack [optional target point]
+.TP
+\fB\-c\fR viewcond
+set source viewing conditions for CIECAM02,
+either an enumerated choice, or a parameter
+.TP
+\fB\-d\fR viewcond
+set destination viewing conditions for CIECAM02,
+either an enumerated choice, or parameter:value changes
+.IP
+pp \- Practical Reflection Print (ISO\-3664 P2)
+pe \- Print evaluation environment (CIE 116\-1995)
+pc \- Critical print evaluation environment (ISO\-3664 P1)
+mt \- Monitor in typical work environment
+mb \- Bright monitor in bright work environment
+md \- Monitor in darkened work environment
+jm \- Projector in dim environment
+jd \- Projector in dark environment
+tv \- Television/Film Studio
+.IP
+pcd \- Photo CD \- original scene outdoors
+.IP
+ob \- Original scene \- Bright Outdoors
+cx \- Cut Sheet Transparencies on a viewing box
+.TP
+s:surround
+n = auto, a = average, m = dim, d = dark,
+c = transparency (default average)
+.TP
+w:X:Y:Z
+Adapted white point as XYZ (default media white)
+.TP
+w:x:y
+Adapted white point as x, y
+.TP
+a:adaptation
+Adaptation luminance in cd.m^2 (default 50.0)
+.TP
+b:background
+Background % of image luminance (default 20)
+.TP
+l:imagewhite
+Image white in cd.m^2 if surround = auto (default 250)
+.TP
+f:flare
+Flare light % of image luminance (default 0)
+.TP
+g:glare
+Flare light % of ambient (default 1)
+.TP
+g:X:Y:Z
+Flare color as XYZ (default media white, Abs: D50)
+.TP
+g:x:y
+Flare color as x, y
+.TP
+\fB\-t\fR tlimit
+set source total ink limit, 0 \- 400% (estimate by default)
+.TP
+\fB\-T\fR klimit
+set source black ink limit, 0 \- 100% (estimate by default)
+.IP
+Inverse outprofile A2B Options:
+\fB\-k\fR tezhxr CMYK Black generation
+.IP
+t = transfer K from source to destination, e = retain K of destination B2A table
+z = zero K, h = 0.5 K, x = maximum K, r = ramp K (default)
+.HP
+\fB\-k\fR p stle stpo enpo enle shape
+.IP
+p = black target generation curve parameters
+.HP
+\fB\-k\fR q stle0 stpo0 enpo0 enle0 shape0 stle2 stpo2 enpo2 enle2 shape2
+.IP
+q = transfer source K to dual curve limits
+.TP
+\fB\-K\fR parameters
+Same as \fB\-k\fR, but target is K locus rather than K value itself
+.TP
+\fB\-l\fR tlimit
+set destination total ink limit, 0 \- 400% (estimate by default)
+.TP
+\fB\-L\fR klimit
+set destination black ink limit, 0 \- 100% (estimate by default)
+.TP
+\fB\-3\fR flag
+Create "3DLut" output file as well as devlink
+.TP
+e
+eeColor .txt file
+.TP
+m
+MadVR .3dlut file
+.TP
+\fB\-I\fR b
+Apply BT.1886\-like mapping with effective gamma 2.2 to input
+.TP
+\fB\-I\fR b:g.g
+Apply BT.1886\-like mapping with effective gamma g.g to input
+.TP
+\fB\-I\fR B
+Apply BT.1886 mapping with technical gamma 2.4 to input
+.TP
+\fB\-I\fR B:g.g
+Apply BT.1886 mapping with technical gamma g.g to input
+.TP
+\fB\-e\fR flag
+Video encode input as:
+.TP
+\fB\-E\fR flag
+Video encode output as:
+.TP
+n
+normal 0..1 full range RGB levels (default)
+.TP
+t
+(16\-235)/255 "TV" RGB levels
+.TP
+6
+Rec601 YCbCr SD (16\-235,240)/255 "TV" levels
+.TP
+7
+Rec709 1125/60Hz YCbCr HD (16\-235,240)/255 "TV" levels
+.TP
+5
+Rec709 1250/50Hz YCbCr HD (16\-235,240)/255 "TV" levels
+.TP
+2
+Rec2020 YCbCr UHD (16\-235,240)/255 "TV" levels
+.TP
+C
+Rec2020 Constant Luminance YCbCr UHD (16\-235,240)/255 "TV" levels
+.TP
+x
+xvYCC Rec601 YCbCr Rec709 Prims. SD (16\-235,240)/255 "TV" levels
+.TP
+X
+xvYCC Rec709 YCbCr Rec709 Prims. HD (16\-235,240)/255 "TV" levels
+.TP
+\fB\-P\fR
+Create gamut gammap.wrl diagostic
diff --git a/debian/man/colprof.1 b/debian/man/colprof.1
new file mode 100644
index 0000000..cf8b9b1
--- /dev/null
+++ b/debian/man/colprof.1
@@ -0,0 +1,186 @@
+.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.44.1.
+.TH CREATE "1" "September 2014" "colprof" "User Commands"
+.SH NAME
+Create \- Create ICC profile.
+.SH DESCRIPTION
+Create ICC profile
+.SH SYNOPSIS
+.B colprof
+.RB [\-options]\ inoutfile
+.TP
+\fB\-v\fR
+Verbose mode
+.HP
+\fB\-A\fR manufacturer Manufacturer description string
+.TP
+\fB\-M\fR model
+Model description string
+.TP
+\fB\-D\fR description
+Profile Description string (Default "inoutfile")
+.TP
+\fB\-C\fR copyright
+Copyright string
+.TP
+\fB\-Z\fR tmnb
+Attributes: Transparency, Matte, Negative, BlackAndWhite
+.TP
+\fB\-Z\fR prsa
+Default intent: Perceptual, Rel. Colorimetric, Saturation, Abs. Colorimetric
+.TP
+\fB\-q\fR lmhu
+Quality \- Low, Medium (def), High, Ultra
+.TP
+\fB\-b\fR [lmhun]
+Low quality B2A table \- or specific B2A quality or none for input device
+.TP
+\fB\-ni\fR
+Don't create input (Device) shaper curves
+.TP
+\fB\-np\fR
+Don't create input (Device) grid position curves
+.TP
+\fB\-no\fR
+Don't create output (PCS) shaper curves
+.TP
+\fB\-nc\fR
+Don't put the input .ti3 data in the profile
+.TP
+\fB\-k\fR zhxr
+Black value target: z = zero K,
+h = 0.5 K, x = max K, r = ramp K (def.)
+.HP
+\fB\-k\fR p stle stpo enpo enle shape
+.IP
+stle: K level at White 0.0 \- 1.0
+stpo: start point of transition Wh 0.0 \- Bk 1.0
+enpo: End point of transition Wh 0.0 \- Bk 1.0
+enle: K level at Black 0.0 \- 1.0
+shape: 1.0 = straight, 0.0\-1.0 concave, 1.0\-2.0 convex
+.TP
+\fB\-K\fR parameters
+Same as \fB\-k\fR, but target is K locus rather than K value itself
+.TP
+\fB\-l\fR tlimit
+override total ink limit, 0 \- 400% (default from .ti3)
+.TP
+\fB\-L\fR klimit
+override black ink limit, 0 \- 100% (default from .ti3)
+.TP
+\fB\-a\fR lxXgsmGS
+Algorithm type override
+l = Lab cLUT (def.), x = XYZ cLUT, X = display XYZ cLUT + matrix
+g = gamma+matrix, s = shaper+matrix, m = matrix only,
+G = single gamma+matrix, S = single shaper+matrix
+.TP
+\fB\-u\fR
+If input profile, auto scale WP to allow extrapolation
+.TP
+\fB\-uc\fR
+If input profile, clip cLUT values above WP
+.TP
+\fB\-U\fR scale
+If input profile, scale media white point by scale
+.TP
+\fB\-R\fR
+Restrict white <= 1.0, black and primaries to be +ve
+.TP
+\fB\-V\fR demphasis
+Degree of dark region cLUT grid emphasis 1.0\-4.0 (default 1.00 = none)
+.TP
+\fB\-f\fR [illum]
+Use Fluorescent Whitening Agent compensation [opt. simulated inst. illum.:
+.IP
+M0, M1, M2, A, C, D50 (def.), D50M2, D65, F5, F8, F10 or file.sp]
+.TP
+\fB\-i\fR illum
+Choose illuminant for computation of CIE XYZ from spectral data & FWA:
+.IP
+A, C, D50 (def.), D50M2, D65, F5, F8, F10 or file.sp
+.TP
+\fB\-o\fR observ
+Choose CIE Observer for spectral data:
+.IP
+1931_2 (def), 1964_10, S&B 1955_2, shaw, J&V 1978_2
+.TP
+\fB\-r\fR avgdev
+Average deviation of device+instrument readings as a percentage (default 0.50%)
+.TP
+\fB\-s\fR src.icc
+Apply gamut mapping to output profile perceptual B2A table for given source space
+.TP
+\fB\-S\fR src.icc
+Apply gamut mapping to output profile perceptual and saturation B2A table
+.TP
+\fB\-nP\fR
+Use colormetric source gamut to make output profile perceptual table
+.TP
+\fB\-nS\fR
+Use colormetric source gamut to make output profile saturation table
+.TP
+\fB\-g\fR src.gam
+Use source image gamut as well for output profile gamut mapping
+.TP
+\fB\-p\fR absprof,...
+Incorporate abstract profile(s) into output tables
+.TP
+\fB\-t\fR intent
+Override gamut mapping intent for output profile perceptual table:
+.TP
+\fB\-T\fR intent
+Override gamut mapping intent for output profile saturation table:
+.IP
+a \- Absolute Colorimetric (in Jab) [ICC Absolute Colorimetric]
+.IP
+aw \- Absolute Colorimetric (in Jab) with scaling to fit white point
+aa \- Absolute Appearance
+.IP
+r \- White Point Matched Appearance [ICC Relative Colorimetric]
+.IP
+la \- Luminance axis matched Appearance
+.IP
+p \- Perceptual (Preferred) (Default) [ICC Perceptual]
+.IP
+pa \- Perceptual Apperance
+ms \- Saturation
+.IP
+s \- Enhanced Saturation [ICC Saturation]
+.IP
+al \- Absolute Colorimetric (Lab)
+rl \- White Point Matched Colorimetric (Lab)
+.TP
+\fB\-c\fR viewcond
+set input viewing conditions for output profile CIECAM02 gamut mapping,
+.IP
+either an enumerated choice, or a parameter
+.TP
+\fB\-d\fR viewcond
+set output viewing conditions for output profile CIECAM02 gamut mapping
+.IP
+either an enumerated choice, or a parameter
+Also sets out of gamut clipping CAM space.
+either an enumerated choice, or a series of parameters:value changes
+.IP
+pp \- Practical Reflection Print (ISO\-3664 P2)
+pe \- Print evaluation environment (CIE 116\-1995)
+pc \- Critical print evaluation environment (ISO\-3664 P1)
+mt \- Monitor in typical work environment
+mb \- Bright monitor in bright work environment
+md \- Monitor in darkened work environment
+jm \- Projector in dim environment
+jd \- Projector in dark environment
+tv \- Television/Film Studio
+.IP
+pcd \- Photo CD \- original scene outdoors
+.IP
+ob \- Original scene \- Bright Outdoors
+cx \- Cut Sheet Transparencies on a viewing box
+.TP
+\fB\-P\fR
+Create gamut gammap_p.wrl and gammap_s.wrl diagostics
+.TP
+\fB\-O\fR outputfile
+Override the default output filename.
+.TP
+inoutfile
+Base name for input.ti3/output.icc file
diff --git a/debian/man/colverify.1 b/debian/man/colverify.1
new file mode 100644
index 0000000..392d8f0
--- /dev/null
+++ b/debian/man/colverify.1
@@ -0,0 +1,68 @@
+.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.44.1.
+.TH VERIFY "1" "September 2014" "verfiy" "User Commands"
+.SH NAME
+Verify \- Verify CIE values.
+.SH DESCRIPTION
+Verify CIE values
+.SH SYNOPSIS
+.B verify
+.RB [\-options]\ target.ti3\ measured.ti3
+.TP
+\fB\-v\fR [n]
+Verbose mode, n >= 2 print each value
+.TP
+\fB\-n\fR
+Normalise each files reading to its white Y
+.TP
+\fB\-N\fR
+Normalise each files reading to its white XYZ
+.TP
+\fB\-m\fR
+Normalise each files reading to its white X+Y+Z
+.TP
+\fB\-D\fR
+Use D50 100.0 as L*a*b* white reference
+.TP
+\fB\-c\fR
+Show CIE94 delta E values
+.TP
+\fB\-k\fR
+Show CIEDE2000 delta E values
+.TP
+\fB\-s\fR
+Sort patch values by error
+.TP
+\fB\-w\fR
+create VRML vector visualisation (measured.wrl)
+.TP
+\fB\-W\fR
+create VRML marker visualisation (measured.wrl)
+.TP
+\fB\-x\fR
+Use VRML axes
+.TP
+\fB\-f\fR [illum]
+Use Fluorescent Whitening Agent compensation [opt. simulated inst. illum.:
+.IP
+M0, M1, M2, A, C, D50 (def.), D50M2, D65, F5, F8, F10 or file.sp]
+.TP
+\fB\-i\fR illum
+Choose illuminant for computation of CIE XYZ from spectral data & FWA:
+.IP
+A, C, D50 (def.), D50M2, D65, F5, F8, F10 or file.sp
+.TP
+\fB\-o\fR observ
+Choose CIE Observer for spectral data:
+1931_2 (def), 1964_10, S&B 1955_2, shaw, J&V 1978_2
+.TP
+\fB\-L\fR profile.icc
+Skip any first file out of profile gamut patches
+.TP
+\fB\-X\fR file.ccmx
+Apply Colorimeter Correction Matrix to second file
+.TP
+target.ti3
+Target (reference) PCS or spectral values.
+.TP
+measured.ti3
+Measured (actual) PCS or spectral values
diff --git a/debian/man/dispcal.1 b/debian/man/dispcal.1
new file mode 100644
index 0000000..20e9442
--- /dev/null
+++ b/debian/man/dispcal.1
@@ -0,0 +1,156 @@
+.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.44.1.
+.TH CALIBRATE "1" "September 2014" "dispcal" "User Commands"
+.SH NAME
+Calibrate \- Calibrate a Display.
+.SH DESCRIPTION
+Calibrate a Display
+.SH SYNOPSIS
+.B dispcal
+.RB [options]\ outfile
+.TP
+\fB\-v\fR [n]
+Verbose mode
+.HP
+\fB\-display\fR displayname Choose X11 display name
+.TP
+\fB\-d\fR n[,m]
+Choose the display n from the following list (default 1)
+Optionally choose different display m for VideoLUT access
+.IP
+1 = 'Screen 1, Output DVI\-I\-1 at 0, 0, width 1920, height 1080'
+.TP
+\fB\-dweb[\fR:port]
+Display via a web server at port (default 8080)
+.TP
+\fB\-c\fR listno
+Set communication port from the following list (default 1)
+.IP
+** No ports found **
+.TP
+\fB\-r\fR
+Report on the calibrated display then exit
+.TP
+\fB\-R\fR
+Report on the uncalibrated display then exit
+.TP
+\fB\-m\fR
+Skip adjustment of the monitor controls
+.TP
+\fB\-o\fR [profile.icc]
+Create fast matrix/shaper profile [different filename to outfile.icc]
+.TP
+\fB\-O\fR "description"
+Fast ICC Profile Description string (Default "outfile")
+.TP
+\fB\-u\fR
+Update previous calibration and (if \fB\-o\fR used) ICC profile VideoLUTs
+.TP
+\fB\-q\fR [vlmh]
+Quality \- Very Low, Low, Medium (def), High
+.TP
+\fB\-p\fR
+Use telephoto mode (ie. for a projector) (if available)
+.TP
+\fB\-t\fR [temp]
+White Daylight locus target, optional target temperaturee in deg. K (deflt.)
+.TP
+\fB\-T\fR [temp]
+White Black Body locus target, optional target temperaturee in deg. K
+.TP
+\fB\-w\fR x,y
+Set the target white point as chromaticity coordinates
+.TP
+\fB\-b\fR bright
+Set the target white brightness in cd/m^2
+.TP
+\fB\-g\fR gamma
+Set the target response curve advertised gamma (Def. 2.4)
+Use "\-gl" for L*a*b* curve
+Use "\-gs" for sRGB curve
+Use "\-g709" for REC 709 curve (should use \fB\-a\fR as well!)
+Use "\-g240" for SMPTE 240M curve (should use \fB\-a\fR as well!)
+Use "\-G2.4 \fB\-f0\fR" for BT.1886
+.TP
+\fB\-G\fR gamma
+Set the target response curve actual technical gamma
+.TP
+\fB\-f\fR [degree]
+Amount of black level accounted for with output offset (default all output offset)
+.TP
+\fB\-a\fR ambient
+Use viewing condition adjustment for ambient in Lux
+.TP
+\fB\-k\fR factor
+Amount to correct black hue, 0 = none, 1 = full, Default = Automatic
+.TP
+\fB\-A\fR rate
+Rate of blending from neutral to black point. Default 4.0
+.TP
+\fB\-B\fR blkbright
+Set the target black brightness in cd/m^2
+.TP
+\fB\-e\fR [n]
+Run n verify passes on final curves
+.TP
+\fB\-z\fR
+Run only verify pass on installed calibration curves
+.TP
+\fB\-P\fR ho,vo,ss[,vs]
+Position test window and scale it
+ho,vi: 0.0 = left/top, 0.5 = center, 1.0 = right/bottom etc.
+ss: 0.5 = half, 1.0 = normal, 2.0 = double etc.
+.TP
+\fB\-F\fR
+Fill whole screen with black background
+.TP
+\fB\-n\fR
+Don't set override redirect on test window
+.TP
+\fB\-E\fR
+Encode the test values for video range 16..235/255
+.TP
+\fB\-J\fR
+Run instrument calibration first (used rarely)
+.TP
+\fB\-N\fR
+Disable initial calibration of instrument if possible
+.TP
+\fB\-H\fR
+Use high resolution spectrum mode (if available)
+.TP
+\fB\-X\fR file.ccmx
+Apply Colorimeter Correction Matrix
+.TP
+\fB\-X\fR file.ccss
+Use Colorimeter Calibration Spectral Samples for calibration
+.TP
+\fB\-Q\fR observ
+Choose CIE Observer for spectrometer or CCSS colorimeter data:
+1931_2 (def), 1964_10, S&B 1955_2, shaw, J&V 1978_2, 1964_10c
+.TP
+\fB\-I\fR b|w
+Drift compensation, Black: \fB\-Ib\fR, White: \fB\-Iw\fR, Both: \fB\-Ibw\fR
+.TP
+\fB\-Y\fR R:rate
+Override measured refresh rate with rate Hz
+.TP
+\fB\-Y\fR A
+Use non\-adaptive integration time mode (if available).
+.TP
+\fB\-Y\fR p
+Don't wait for the instrument to be placed on the display
+.TP
+\fB\-C\fR "command"
+Invoke shell "command" each time a color is set
+.TP
+\fB\-M\fR "command"
+Invoke shell "command" each time a color is measured
+.TP
+\fB\-W\fR n|h|x
+Override serial port flow control: n = none, h = HW, x = Xon/Xoff
+.TP
+\fB\-D\fR [level]
+Print debug diagnostics to stderr
+.TP
+inoutfile
+Base name for created or updated .cal and .icc output files
diff --git a/debian/man/dispread.1 b/debian/man/dispread.1
new file mode 100644
index 0000000..a19b8c7
--- /dev/null
+++ b/debian/man/dispread.1
@@ -0,0 +1,103 @@
+.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.44.1.
+.TH READ "1" "September 2014" "dispread" "User Commands"
+.SH NAME
+Read \- Read a Display.
+.SH DESCRIPTION
+Read a Display
+.SH SYNOPSIS
+.B dispread
+.RB [options]\ outfile
+.TP
+\fB\-v\fR
+Verbose mode
+.HP
+\fB\-display\fR displayname Choose X11 display name
+.TP
+\fB\-d\fR n[,m]
+Choose the display n from the following list (default 1)
+Optionally choose different display m for VideoLUT access
+.IP
+1 = 'Screen 1, Output DVI\-I\-1 at 0, 0, width 1920, height 1080'
+.TP
+\fB\-dweb[\fR:port]
+Display via a web server at port (default 8080)
+.TP
+\fB\-c\fR listno
+Set communication port from the following list (default 1)
+.IP
+** No ports found **
+.TP
+\fB\-p\fR
+Use telephoto mode (ie. for a projector) (if available)
+.TP
+\fB\-k\fR file.cal
+Load calibration file into display while reading
+.TP
+\fB\-K\fR file.cal
+Apply calibration file to test values while reading
+.TP
+\fB\-s\fR
+Save spectral information (default don't save)
+.TP
+\fB\-P\fR ho,vo,ss[,vs]
+Position test window and scale it
+ho,vi: 0.0 = left/top, 0.5 = center, 1.0 = right/bottom etc.
+ss: 0.5 = half, 1.0 = normal, 2.0 = double etc.
+.TP
+\fB\-F\fR
+Fill whole screen with black background
+.TP
+\fB\-n\fR
+Don't set override redirect on test window
+.TP
+\fB\-E\fR
+Encode the test values for video range 16..235/255
+.TP
+\fB\-J\fR
+Run instrument calibration first (used rarely)
+.TP
+\fB\-N\fR
+Disable initial calibration of instrument if possible
+.TP
+\fB\-H\fR
+Use high resolution spectrum mode (if available)
+.TP
+\fB\-w\fR
+Disable normalisation of white to Y = 100
+.TP
+\fB\-X\fR file.ccmx
+Apply Colorimeter Correction Matrix
+.TP
+\fB\-X\fR file.ccss
+Use Colorimeter Calibration Spectral Samples for calibration
+.TP
+\fB\-Q\fR observ
+Choose CIE Observer for spectrometer or CCSS colorimeter data:
+1931_2 (def), 1964_10, S&B 1955_2, shaw, J&V 1978_2, 1964_10c
+.TP
+\fB\-I\fR b|w
+Drift compensation, Black: \fB\-Ib\fR, White: \fB\-Iw\fR, Both: \fB\-Ibw\fR
+.TP
+\fB\-Y\fR R:rate
+Override measured refresh rate with rate Hz
+.TP
+\fB\-Y\fR A
+Use non\-adaptive integration time mode (if available).
+.TP
+\fB\-Y\fR p
+Don't wait for the instrument to be placed on the display
+.TP
+\fB\-C\fR "command"
+Invoke shell "command" each time a color is set
+.TP
+\fB\-M\fR "command"
+Invoke shell "command" each time a color is measured
+.TP
+\fB\-W\fR n|h|x
+Override serial port flow control: n = none, h = HW, x = Xon/Xoff
+.TP
+\fB\-D\fR [level]
+Print debug diagnostics to stderr
+.TP
+outfile
+Base name for input[ti1]/output[ti3] file
diff --git a/debian/man/dispwin.1 b/debian/man/dispwin.1
new file mode 100644
index 0000000..c4c7594
--- /dev/null
+++ b/debian/man/dispwin.1
@@ -0,0 +1,81 @@
+.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.44.1.
+.TH TEST "1" "September 2014" "dispwin" "User Commands"
+.SH NAME
+Test \- Test display patch window, Set Video LUTs, Install profiles.
+.SH DESCRIPTION
+Test display patch window, Set Video LUTs, Install profiles
+.SH SYNOPSIS
+.B dispwin
+.RB [options]\ [calfile]
+.TP
+\fB\-v\fR
+Verbose mode
+.HP
+\fB\-display\fR displayname Choose X11 display name
+.TP
+\fB\-d\fR n[,m]
+Choose the display n from the following list (default 1)
+Optionally choose different display m for Video LUT access
+.IP
+1 = 'Screen 1, Output DVI\-I\-1 at 0, 0, width 1920, height 1080'
+.TP
+\fB\-dweb[\fR:port]
+Display via a web server at port (default 8080)
+.TP
+\fB\-P\fR ho,vo,ss[,vs]
+Position test window and scale it
+.TP
+\fB\-F\fR
+Fill whole screen with black background
+.TP
+\fB\-i\fR
+Run forever with random values
+.TP
+\fB\-G\fR filename
+Display RGB colors from CGATS file
+.TP
+\fB\-C\fR r.rr,g.gg,b.bb
+Add this RGB color to list to be displayed
+.TP
+\fB\-m\fR
+Manually cycle through values
+.TP
+\fB\-f\fR
+Test grey ramp fade
+.TP
+\fB\-r\fR
+Test just Video LUT loading & Beeps
+.TP
+\fB\-n\fR
+Test native output (rather than through Video LUT and C.M.)
+.TP
+\fB\-s\fR filename
+Save the currently loaded Video LUT to 'filename'
+.TP
+\fB\-c\fR
+Load a linear display calibration
+.TP
+\fB\-V\fR
+Verify that calfile/profile cal. is currently loaded in LUT
+.TP
+\fB\-I\fR
+Install profile for display and use it's calibration
+.TP
+\fB\-U\fR
+Un\-install profile for display
+.TP
+\fB\-S\fR d
+Specify the install/uninstall scope for OS X [nlu] or X11/Vista [lu]
+d is one of: n = network, l = local system, u = user (default)
+.TP
+\fB\-L\fR
+Load installed profiles cal. into Video LUT
+.TP
+\fB\-X\fR
+Run in daemon loader mode for given X11 server
+.TP
+\fB\-D\fR [level]
+Print debug diagnostics to stderr
+.TP
+calfile
+Load calibration (.cal or .icc) into Video LUT
diff --git a/debian/man/extracticc.1 b/debian/man/extracticc.1
new file mode 100644
index 0000000..9d5adad
--- /dev/null
+++ b/debian/man/extracticc.1
@@ -0,0 +1,12 @@
+.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.44.1.
+.TH EXTRACT "1" "September 2014" "extracticc" "User Commands"
+.SH NAME
+Extract \- Extract an ICC profile from a TIFF file.
+.SH DESCRIPTION
+Extract an ICC profile from a TIFF or JPEG file
+.SH SYNOPSIS
+.B extracticc
+.RB [\-v]\ infile.(tif|jpg)\ outfile.icc
+.TP
+\fB\-v\fR
+Verbose
diff --git a/debian/man/extractttag.1 b/debian/man/extractttag.1
new file mode 100644
index 0000000..eac947a
--- /dev/null
+++ b/debian/man/extractttag.1
@@ -0,0 +1,18 @@
+.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.44.1.
+.TH EXTRACT "1" "September 2014" "extractttag" "User Commands"
+.SH NAME
+Extract \- Extract a text tag from an ICC profile.
+.SH DESCRIPTION
+Extract a text tag from an ICC profile
+.SH SYNOPSIS
+.B extractttag
+.RB [\-v]\ infile.icc\ outfile
+.TP
+\fB\-v\fR
+Verbose
+.TP
+\fB\-t\fR tag
+Extract this tag rather than default 'targ'
+.TP
+\fB\-c\fR
+Extract calibration file from 'targ' tag
diff --git a/debian/man/fakeCMY.1 b/debian/man/fakeCMY.1
new file mode 100644
index 0000000..6509ad6
--- /dev/null
+++ b/debian/man/fakeCMY.1
@@ -0,0 +1,21 @@
+.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.44.1.
+.TH CREATE "1" "September 2014" "fakeCMY" "User Commands"
+.SH NAME
+Create \- Create a fake CMY data file from a CMYK profile.
+.SH DESCRIPTION
+Create a fake CMY data file from a CMYK profile
+.SH SYNOPSIS
+.B fakeCMY
+.RB [option]\ profile.icm\ fake.ti3
+.TP
+\fB\-v\fR
+verbose
+.TP
+\fB\-r\fR res
+set surface point resolution (default 3)
+.TP
+\fB\-l\fR tlimit
+set total ink limit, 0 \- 400% (estimate by default)
+.TP
+\fB\-L\fR klimit
+set black ink limit, 0 \- 100% (estimate by default)
diff --git a/debian/man/fakeread.1 b/debian/man/fakeread.1
new file mode 100644
index 0000000..1af12e0
--- /dev/null
+++ b/debian/man/fakeread.1
@@ -0,0 +1,90 @@
+.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.44.1.
+.TH FAKE "1" "September 2014" "fakeread" "User Commands"
+.SH NAME
+Fake \- Fake test chart reader - lookup values in ICC/MPP profile.
+.SH DESCRIPTION
+Fake test chart reader \- lookup values in ICC/MPP profile
+.SH SYNOPSIS
+.B fakeread
+.RB [\-options]\ profile.[icc|mpp|ti3]\ outfile
+.TP
+\fB\-v\fR [n]
+Verbose mode [level]
+.TP
+\fB\-e\fR flag
+Video encode device input to sepration as:
+.TP
+n
+normal 0..1 full range RGB levels (default)
+.TP
+t
+(16\-235)/255 "TV" RGB levels
+.TP
+6
+Rec601 YCbCr SD (16\-235,240)/255 "TV" levels
+.TP
+7
+Rec709 1125/60Hz YCbCr HD (16\-235,240)/255 "TV" levels
+.TP
+5
+Rec709 1250/50Hz YCbCr HD (16\-235,240)/255 "TV" levels
+.TP
+2
+Rec2020 YCbCr UHD (16\-235,240)/255 "TV" levels
+.TP
+C
+Rec2020 Constant Luminance YCbCr UHD (16\-235,240)/255 "TV" levels
+.HP
+\fB\-p\fR separation.icc Use device link separation profile on input
+.TP
+\fB\-E\fR flag
+Video decode separation device output. See \fB\-e\fR above
+.TP
+\fB\-k\fR file.cal
+Apply calibration (include in .ti3 output)
+.TP
+\fB\-i\fR file.cal
+Include calibration in .ti3 output, but don't apply it
+.TP
+\fB\-K\fR file.cal
+Apply inverse calibration
+.TP
+\fB\-r\fR level
+Add average random deviation of <level>% to device values (after sep. & cal.)
+.TP
+\fB\-0\fR pow
+Apply power to device chanel 0\-9
+.TP
+\fB\-b\fR output.icc
+Apply BT.1886\-like mapping with effective gamma 2.2
+.HP
+\fB\-b\fR g.g:output.icc Apply BT.1886\-like mapping with effective gamma g.g
+.TP
+\fB\-B\fR output.icc
+Apply BT.1886 mapping with technical gamma 2.4
+.HP
+\fB\-B\fR g.g:output.icc Apply BT.1886 mapping with technical gamma g.g
+.TP
+\fB\-I\fR intent
+r = relative colorimetric, a = absolute (default)
+.TP
+\fB\-A\fR L,a,b
+Scale black point to target Lab value
+.TP
+\fB\-l\fR
+Output Lab rather than XYZ
+.TP
+\fB\-s\fR
+Lookup MPP spectral values
+.TP
+\fB\-R\fR level
+Add average random deviation of <level>% to output PCS values
+.TP
+\fB\-u\fR
+Make random deviations have uniform distributions rather than normal
+.TP
+\fB\-S\fR seed
+Set random seed
+.IP
+profile.[icc|mpp|ti3] ICC, MPP profile or TI3 to use
+outfile Base name for input[ti1]/output[ti3] file
diff --git a/debian/man/greytiff.1 b/debian/man/greytiff.1
new file mode 100644
index 0000000..f1b71d6
--- /dev/null
+++ b/debian/man/greytiff.1
@@ -0,0 +1,18 @@
+.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.44.1.
+.TH CONVERT "1" "September 2014" "greytiff" "User Commands"
+.SH NAME
+Convert \- Convert a TIFF file to monochrome using an ICC device profile.
+.SH DESCRIPTION
+Convert a TIFF file to monochrome using an ICC device profile
+.SH SYNOPSIS
+.B greytiff
+.RB [\-v\ level]\ profile.icm\ infile.tif\ outfile.tif
+.TP
+\fB\-v\fR
+Verbose
+.TP
+\fB\-p\fR
+Use slow precise correction
+.TP
+\fB\-j\fR
+Use CIECAM02
diff --git a/debian/man/iccdump.1 b/debian/man/iccdump.1
new file mode 100644
index 0000000..e508f13
--- /dev/null
+++ b/debian/man/iccdump.1
@@ -0,0 +1,21 @@
+.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.44.1.
+.TH DUMP "1" "September 2014" "iccdump" "User Commands"
+.SH NAME
+Dump \- Dump an ICC file in human readable form.
+.SH DESCRIPTION
+Dump an ICC file in human readable form
+.SH SYNOPSIS
+.B iccdump
+.RB [\-v\ level]\ [\-t\ tagname]\ [\-s]\ infile
+.TP
+\fB\-v\fR level
+Verbose level 1\-3 (default 2)
+.TP
+\fB\-t\fR tag
+Dump this tag only (can be used multiple times)
+.TP
+\fB\-s\fR
+Search for embedded profile
+.TP
+\fB\-i\fR
+Check V4 ID value
diff --git a/debian/man/iccgamut.1 b/debian/man/iccgamut.1
new file mode 100644
index 0000000..2aa40ff
--- /dev/null
+++ b/debian/man/iccgamut.1
@@ -0,0 +1,97 @@
+.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.44.1.
+.TH CREATE "1" "September 2014" "iccgamut" "User Commands"
+.SH NAME
+Create \- Create Lab/Jab gamut plot.
+.SH DESCRIPTION
+Create Lab/Jab gamut plot
+.SH SYNOPSIS
+.B iccgamut
+.RB [options]\ profile
+.TP
+\fB\-v\fR
+Verbose
+.TP
+\fB\-d\fR sres
+Surface resolution details 1.0 \- 50.0
+.TP
+\fB\-w\fR
+emit VRML .wrl file as well as CGATS .gam file
+.TP
+\fB\-n\fR
+Don't add VRML axes or white/black point
+.TP
+\fB\-k\fR
+Add VRML markers for prim. & sec. "cusp" points
+.TP
+\fB\-f\fR function
+f = forward*, b = backwards
+.TP
+\fB\-i\fR intent
+p = perceptual, r = relative colorimetric,
+s = saturation, a = absolute (default), d = profile default
+.TP
+\fB\-p\fR oride
+l = Lab_PCS (default), j = CIECAM02 Appearance Jab
+.TP
+\fB\-o\fR order
+n = normal (priority: lut > matrix > monochrome)
+r = reverse (priority: monochrome > matrix > lut)
+.TP
+\fB\-l\fR tlimit
+set total ink limit, 0 \- 400% (estimate by default)
+.TP
+\fB\-L\fR klimit
+set black ink limit, 0 \- 100% (estimate by default)
+.TP
+\fB\-c\fR viewcond
+set viewing conditions for CIECAM02,
+either an enumerated choice, or a series of parameter:value changes
+.IP
+pp \- Practical Reflection Print (ISO\-3664 P2)
+pe \- Print evaluation environment (CIE 116\-1995)
+pc \- Critical print evaluation environment (ISO\-3664 P1)
+mt \- Monitor in typical work environment
+mb \- Bright monitor in bright work environment
+md \- Monitor in darkened work environment
+jm \- Projector in dim environment
+jd \- Projector in dark environment
+tv \- Television/Film Studio
+.IP
+pcd \- Photo CD \- original scene outdoors
+.IP
+ob \- Original scene \- Bright Outdoors
+cx \- Cut Sheet Transparencies on a viewing box
+.TP
+s:surround
+n = auto, a = average, m = dim, d = dark,
+c = transparency (default average)
+.TP
+w:X:Y:Z
+Adapted white point as XYZ (default media white)
+.TP
+w:x:y
+Adapted white point as x, y
+.TP
+a:adaptation
+Adaptation luminance in cd.m^2 (default 50.0)
+.TP
+b:background
+Background % of image luminance (default 20)
+.TP
+l:imagewhite
+Image white in cd.m^2 if surround = auto (default 250)
+.TP
+f:flare
+Flare light % of image luminance (default 0)
+.TP
+g:glare
+Flare light % of ambient (default 1)
+.TP
+g:X:Y:Z
+Flare color as XYZ (default media white, Abs: D50)
+.TP
+g:x:y
+Flare color as x, y
+.TP
+\fB\-s\fR
+Create special cube surface topology plot
diff --git a/debian/man/icclu.1 b/debian/man/icclu.1
new file mode 100644
index 0000000..5cccdde
--- /dev/null
+++ b/debian/man/icclu.1
@@ -0,0 +1,34 @@
+.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.44.1.
+.TH TRANSLATE "1" "September 2014" "icclu" "User Commands"
+.SH NAME
+Translate \- Translate colors through an ICC profile.
+.SH DESCRIPTION
+Translate colors through an ICC profile
+.SH SYNOPSIS
+.B icclu
+.RB [\-v\ level]\ [\-f\ func]\ [\-i\ intent]\ [\-o\ order]\ profile
+.TP
+\fB\-v\fR level
+Verbosity level 0 \- 2 (default = 1)
+.TP
+\fB\-f\fR function
+f = forward, b = backwards, g = gamut, p = preview
+.TP
+\fB\-i\fR intent
+p = perceptual, r = relative colorimetric,
+s = saturation, a = absolute
+.TP
+\fB\-p\fR oride
+x = XYZ_PCS, l = Lab_PCS, y = Yxy,
+.TP
+\fB\-o\fR order
+n = normal (priority: lut > matrix > monochrome)
+r = reverse (priority: monochrome > matrix > lut)
+.TP
+\fB\-s\fR scale
+Scale device range 0.0 \- scale rather than 0.0 \- 1.0
+.IP
+The colors to be translated should be fed into standard input,
+one input color per line, white space separated.
+A line starting with a # will be ignored.
+A line not starting with a number will terminate the program.
diff --git a/debian/man/illumread.1 b/debian/man/illumread.1
new file mode 100644
index 0000000..aaec8c3
--- /dev/null
+++ b/debian/man/illumread.1
@@ -0,0 +1,41 @@
+.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.44.1.
+.TH MEASURE "1" "September 2014" "illumread" "User Commands"
+.SH NAME
+Measure \- Measure an illuminant.
+.SH DESCRIPTION
+Measure an illuminant
+.SH SYNOPSIS
+.B illumread
+.RB [\-options]\ output.sp
+.TP
+\fB\-v\fR
+Verbose mode
+.TP
+\fB\-S\fR
+Plot spectrum for each reading
+.TP
+\fB\-c\fR listno
+Choose instrument from the following list (default 1)
+.IP
+** No ports found **
+.TP
+\fB\-N\fR
+Disable initial calibration of instrument if possible
+.TP
+\fB\-H\fR
+Use high resolution spectrum mode (if available)
+.TP
+\fB\-Y\fR r
+Set refresh measurement mode
+.TP
+\fB\-Y\fR R:rate
+Override measured refresh rate with rate Hz
+.TP
+\fB\-W\fR n|h|x
+Override serial port flow control: n = none, h = HW, x = Xon/Xoff
+.TP
+\fB\-D\fR [level]
+Print debug diagnostics to stderr
+.TP
+illuminant.sp
+File to save measurement to
diff --git a/debian/man/invprofcheck.1 b/debian/man/invprofcheck.1
new file mode 100644
index 0000000..60d897b
--- /dev/null
+++ b/debian/man/invprofcheck.1
@@ -0,0 +1,45 @@
+.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.44.1.
+.TH CHECK "1" "September 2014" "invprofcheck" "User Commands"
+.SH NAME
+Check \- Check fwd to bwd relative transfer of an ICC file.
+.SH DESCRIPTION
+Check fwd to bwd relative transfer of an ICC file
+.SH SYNOPSIS
+.B invprofcheck
+.RB [\-]\ profile.icm
+.TP
+\fB\-v\fR [level]
+verbosity level (default 1), 2 to print each DE
+.TP
+\fB\-l\fR limit
+set total ink limit (estimate by default)
+.TP
+\fB\-L\fR klimit
+set black channel ink limit (estimate by default)
+.TP
+\fB\-h\fR
+high res test (27)
+.TP
+\fB\-u\fR
+Ultra high res test (61)
+.TP
+\fB\-R\fR res
+Specific grid resolution
+.TP
+\fB\-c\fR
+Show CIE94 delta E values
+.TP
+\fB\-k\fR
+Show CIEDE2000 delta E values
+.TP
+\fB\-w\fR
+create VRML visualisation (profile.wrl)
+.TP
+\fB\-x\fR
+Use VRML axes
+.TP
+\fB\-e\fR
+Color vectors according to delta E
+.TP
+profile.icm
+Profile to check
diff --git a/debian/man/kodak2ti3.1 b/debian/man/kodak2ti3.1
new file mode 100644
index 0000000..45abd06
--- /dev/null
+++ b/debian/man/kodak2ti3.1
@@ -0,0 +1,24 @@
+.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.44.1.
+.TH CONVERT "1" "September 2014" "kodak2ti3" "User Commands"
+.SH NAME
+Convert \- Convert Kodak raw printer profile data to Argyll print data.
+.SH DESCRIPTION
+Convert Kodak raw printer profile data to Argyll print data
+.SH SYNOPSIS
+.B kodak2ti3
+.RB [\-v]\ [\-l\ limit]\ infile\ outfile
+.TP
+\fB\-v\fR
+Verbose mode
+.TP
+\fB\-l\fR limit
+set ink limit, 0 \- 400%
+.TP
+\fB\-r\fR filename
+Use an alternate 928 patch reference file
+.TP
+infile
+Base name for input.pat file
+.TP
+outfile
+Base name for output.ti3 file
diff --git a/debian/man/ls2ti3.1 b/debian/man/ls2ti3.1
new file mode 100644
index 0000000..d7c7315
--- /dev/null
+++ b/debian/man/ls2ti3.1
@@ -0,0 +1,13 @@
+.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.46.6.
+.TH CONVERT "1" "May 2015" "Convert LightSpace raw RGB device profile data to Argyll CGATS data, Version 1.7.0" "User Commands"
+.SH NAME
+Convert \- Convert LightSpace raw RGB device profile data to Argyll CGATS data
+.SH DESCRIPTION
+Convert LightSpace raw RGB device profile data to Argyll CGATS data, Version 1.7.0
+Author: Graeme W. Gill, licensed under the AGPL Version 3
+.TP
+infile
+Input LightSpace .bcs file
+.TP
+outbasename
+Output file basename for .ti3
diff --git a/debian/man/mppcheck.1 b/debian/man/mppcheck.1
new file mode 100644
index 0000000..1cd6b16
--- /dev/null
+++ b/debian/man/mppcheck.1
@@ -0,0 +1,29 @@
+.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.44.1.
+.TH CHECK "1" "September 2014" "mppcheck" "User Commands"
+.SH NAME
+Check \- Check Model Printer Profile.
+.SH DESCRIPTION
+Check Model Printer Profile
+.SH SYNOPSIS
+.B mppcheck
+.RB [\-v]\ [\-c]\ [\-s]\ [\-y]\ values.ti3\ profile.mpp
+.TP
+\fB\-v\fR
+Verbose mode
+.TP
+\fB\-c\fR
+Show CIE94 delta E values
+.TP
+\fB\-k\fR
+Show CIEDE2000 delta E values
+.TP
+\fB\-s\fR
+Check spectral model too
+.TP
+\fB\-y\fR
+Detail each value
+.TP
+values.ti3
+Test values to check against
+.IP
+profile.mpp Profile to check
diff --git a/debian/man/mpplu.1 b/debian/man/mpplu.1
new file mode 100644
index 0000000..5651f26
--- /dev/null
+++ b/debian/man/mpplu.1
@@ -0,0 +1,57 @@
+.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.44.1.
+.TH TRANSLATE "1" "September 2014" "" "User Commands"
+.SH NAME
+Translate \- Translate colors through an MPP profile.
+.SH DESCRIPTION
+Translate colors through an MPP profile
+.SH SYNOPSIS
+.B mpplu
+.RB [\-v]\ [\-f\ func]\ [\-i\ intent]\ [\-o\ order]\ profile
+.TP
+\fB\-v\fR
+Verbose
+.TP
+\fB\-f\fR function
+f = forward, b = backwards
+.TP
+\fB\-p\fR oride
+x = XYZ_PCS, l = Lab_PCS, y = Yxy, s = spectral,
+.TP
+\fB\-l\fR limit
+override default ink limit, 1 \- N00%
+.TP
+\fB\-i\fR illum
+Choose illuminant for print/transparency spectral data:
+A, C, D50 (def.), D50M2, D65, F5, F8, F10 or file.sp
+.TP
+\fB\-o\fR observ
+Choose CIE Observer for spectral data:
+1931_2 (def), 1964_10, S&B 1955_2, shaw, J&V 1978_2
+.TP
+\fB\-u\fR
+Use Fluorescent Whitening Agent compensation
+.TP
+\fB\-g\fR
+Create gamut output
+.TP
+\fB\-w\fR
+Create gamut VRML as well
+.TP
+\fB\-n\fR
+Don't add VRML axes
+.TP
+\fB\-a\fR n
+Gamut transparency level
+.TP
+\fB\-d\fR n
+Gamut surface detail level
+.TP
+\fB\-t\fR num
+Invoke debugging test code "num" 1..n
+1 \- check partial derivative for device input
+2 \- create overlap diagnostic VRML gamut surface
+.IP
+The colors to be translated should be fed into stdin,
+one input color per line, white space separated.
+A line starting with a # will be ignored.
+A line not starting with a number will terminate the program.
diff --git a/debian/man/mppprof.1 b/debian/man/mppprof.1
new file mode 100644
index 0000000..13a18ef
--- /dev/null
+++ b/debian/man/mppprof.1
@@ -0,0 +1,30 @@
+.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.44.1.
+.TH CREATE "1" "September 2014" "mppprof" "User Commands"
+.SH NAME
+Create \- Create Model Printer Profile.
+.SH DESCRIPTION
+Create Model Printer Profile
+.SH SYNOPSIS
+.B mppprof
+.RB [options]\ outfile
+.HP
+\fB\-v\fR [level] Verbose mode
+.HP
+\fB\-q\fR [lmhus] Quality \- Low, Medium (def), High, Ultra, Simple
+.TP
+\fB\-l\fR limit
+override default ink limit, 1 \- n00%
+.TP
+\fB\-s\fR
+Generate spectral model too
+.TP
+\fB\-m\fR
+Generate ink mixing model
+.HP
+\fB\-y\fR [level] Verify profile, 2 = read/write verify
+.TP
+\fB\-L\fR
+Output Lab values
+.TP
+outfile
+Base name for input.ti3/output.mpp file
diff --git a/debian/man/oeminst.1 b/debian/man/oeminst.1
new file mode 100644
index 0000000..ea56505
--- /dev/null
+++ b/debian/man/oeminst.1
@@ -0,0 +1,28 @@
+.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.44.1.
+.TH INSTALL "1" "September 2014" "oeminst" "User Commands"
+.SH NAME
+Install \- List information about the FILEs.
+.SH DESCRIPTION
+Install OEM data files
+.SH SYNOPSIS
+.B oeminst
+.RB [\-options]\ [infile(s)]
+.TP
+\fB\-v\fR [level]
+Verbose
+.TP
+\fB\-n\fR
+Don't install, show where files would be installed
+.TP
+\fB\-c\fR
+Don't install, save files to current directory
+.TP
+\fB\-S\fR d
+Specify the install scope u = user (def.), l = local system]
+.TP
+infile
+setup.exe CD install file(s) or .dll(s) containing install files
+.TP
+infile.[edr|ccss|ccmx]
+EDR file(s) to translate and install or CCSS or CCMX files to install
+If no file is provided, oeminst will look for the install CD.
diff --git a/debian/man/printcal.1 b/debian/man/printcal.1
new file mode 100644
index 0000000..38038d6
--- /dev/null
+++ b/debian/man/printcal.1
@@ -0,0 +1,77 @@
+.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.44.1.
+.TH CREATE "1" "September 2014" "printcal" "User Commands"
+.SH NAME
+Create \- Create printer calibration.
+.SH DESCRIPTION
+Create printer calibration
+.SH SYNOPSIS
+.B printcal
+.RB [\-options]\ [prevcal]\ inoutfile
+.TP
+\fB\-v\fR verbosity
+Verbose mode
+.TP
+\fB\-p\fR
+Plot graphs.
+.TP
+\fB\-i\fR
+Initial calibration, set targets, create .cal
+.TP
+\fB\-r\fR
+Re\-calibrate against previous .cal and create new .cal
+.TP
+\fB\-e\fR
+Verify against previous .cal
+.TP
+\fB\-I\fR
+Create imitation target from .ti3 and null calibration
+.TP
+\fB\-d\fR
+Go through the motions but don't write any files
+.TP
+\fB\-s\fR smoothing
+Extra curve smoothing (default 1.0)
+.HP
+\fB\-A\fR manufacturer Set the manufacturer description string
+.TP
+\fB\-M\fR model
+Set the model description string
+.TP
+\fB\-D\fR description
+Set the profile Description string
+.TP
+\fB\-C\fR copyright
+Set the copyright string
+.TP
+\fB\-x\fR# percent
+Set initial maximum device % target (override auto)
+.TP
+\fB\-m\fR# percent
+Set initial dev target to % of auto maximum
+.TP
+\fB\-n\fR# deltaE
+Set initial white minimum deltaE target
+.TP
+\fB\-t\fR# percent
+Set initial 50% transfer curve percentage target
+.TP
+# = c, r, 0
+First channel
+.TP
+m, g, 1
+Second channel
+.TP
+y, b, 2
+Third channel
+.TP
+k,
+3 Fourth channel, etc.
+.TP
+\fB\-a\fR
+Create an Adobe Photoshop .AMP file as well as a .cal
+.TP
+prevcal
+Base name of previous .cal file for recal or verify.
+.TP
+inoutname
+Base name of input .ti3 file, output .cal file
diff --git a/debian/man/printtarg.1 b/debian/man/printtarg.1
new file mode 100644
index 0000000..59ad2ba
--- /dev/null
+++ b/debian/man/printtarg.1
@@ -0,0 +1,125 @@
+.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.44.1.
+.TH GENERATE "1" "September 2014" "printarg" "User Commands"
+.SH NAME
+Generate \- Generate Target PostScrip file.
+.SH DESCRIPTION
+Generate Target PostScrip file
+.SH SYNOPSIS
+.B printtarg
+.RB [\-v]\ [\-i\ instr]\ [\-r]\ [\-s]\ [\-p\ size]\ basename
+.TP
+\fB\-v\fR
+Verbose mode
+.HP
+\fB\-i\fR 20 | 22 | 41 | 51 | SS | i1 | CM Select target instrument (default DTP41)
+.IP
+20 = DTP20, 22 = DTP22, 41 = DTP41, 51 = DTP51,
+SS = SpectroScan, i1 = i1Pro, CM = ColorMunki
+.TP
+\fB\-h\fR
+Use hexagon patches for SS, double density for CM
+.TP
+\fB\-a\fR scale
+Scale patch size and spacers by factor (e.g. 0.857 or 1.5 etc.)
+.TP
+\fB\-A\fR scale
+Scale spacers by additional factor (e.g. 0.857 or 1.5 etc.)
+.TP
+\fB\-r\fR
+Don't randomize patch location
+.TP
+\fB\-s\fR
+Create a scan image recognition (.cht) file
+.TP
+\fB\-S\fR
+Same as \fB\-s\fR, but don't generate wide orientation strip.
+.TP
+\fB\-c\fR
+Force colored spacers
+.TP
+\fB\-b\fR
+Force B&W spacers
+.TP
+\fB\-n\fR
+Force no spacers
+.TP
+\fB\-f\fR
+Create PostScript DeviceN Color fallback
+.TP
+\fB\-w\fR g|r|s|n
+White colorspace encoding DeviceGray (def), DeviceRGB, Separation or DeviceN
+.TP
+\fB\-k\fR g|c|s|n
+Black colorspace encoding DeviceGray (def), DeviceCMYK, Separation or DeviceN
+.TP
+\fB\-o\fR k|r|n
+CMY colorspace encoding DefiveCMYK (def), inverted DeviceRGB or DeviceN
+.TP
+\fB\-e\fR
+Output EPS compatible file
+.TP
+\fB\-t\fR [res]
+Output 8 bit TIFF raster file, optional res DPI (default 100)
+.TP
+\fB\-T\fR [res]
+Output 16 bit TIFF raster file, optional res DPI (default 100)
+.TP
+\fB\-C\fR
+Don't use TIFF compression
+.TP
+\fB\-N\fR
+Use TIFF alpha N channels more than 4
+.TP
+\fB\-D\fR
+Dither 8 bit TIFF values down from 16 bit
+.TP
+\fB\-Q\fR nbits
+Quantize test values to fit in nbits
+.TP
+\fB\-R\fR rsnum
+Use given random start number
+.TP
+\fB\-K\fR file.cal
+Apply printer calibration to patch values and include in .ti2
+.TP
+\fB\-I\fR file.cal
+Include calibration in .ti2 (but don't apply it)
+.TP
+\fB\-x\fR pattern
+Use given strip indexing pattern (Default = "A\-Z, A\-Z")
+.TP
+\fB\-y\fR pattern
+Use given patch indexing pattern (Default = "0\-9,@\-9,@\-9;1\-999")
+.TP
+\fB\-m\fR margin
+Set a page margin in mm (default 6.0 mm)
+.TP
+\fB\-M\fR margin
+Set a page margin in mm and include it in TIFF
+.TP
+\fB\-P\fR
+Don't limit strip length
+.TP
+\fB\-L\fR
+Suppress any left paper clip border
+.TP
+\fB\-U\fR
+Suppress CUPS cupsJobTicket: cups\-disable\-cmm in PS & EPS files
+.TP
+\fB\-p\fR size
+Select page size from:
+A4 [210.0 x 297.0 mm]
+A4R [297.0 x 210.0 mm]
+A3 [297.0 x 420.0 mm] (default)
+A2 [420.0 x 594.0 mm]
+Letter [215.9 x 279.4 mm]
+LetterR [279.4 x 215.9 mm]
+Legal [215.9 x 355.6 mm]
+4x6 [101.6 x 152.4 mm]
+11x17 [279.4 x 431.8 mm]
+.TP
+\fB\-p\fR WWWxHHH
+Custom size, WWW mm wide by HHH mm high
+.TP
+basname
+Base name for input(.ti1), output(.ti2) and output(.ps/.eps/.tif)
diff --git a/debian/man/profcheck.1 b/debian/man/profcheck.1
new file mode 100644
index 0000000..683786a
--- /dev/null
+++ b/debian/man/profcheck.1
@@ -0,0 +1,61 @@
+.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.44.1.
+.TH CHECK "1" "September 2014" "profcheck" "User Commands"
+.SH NAME
+Check \- Check accuracy of ICC profile.
+.SH DESCRIPTION
+Check accuracy of ICC profile, Version 1.6.3
+.SH SYNOPSIS
+.B profcheck
+.RB [\-options]\ data.ti3\ iccprofile.icm
+.TP
+\fB\-v\fR [level]
+Verbosity level (default 1), 2 to print each DE
+.TP
+\fB\-c\fR
+Show CIE94 delta E values
+.TP
+\fB\-k\fR
+Show CIEDE2000 delta E values
+.TP
+\fB\-w\fR
+create VRML visualisation (iccprofile.wrl)
+.TP
+\fB\-x\fR
+Use VRML axes
+.TP
+\fB\-m\fR
+Make VRML lines a minimum of 0.5
+.TP
+\fB\-e\fR
+Color vectors according to delta E
+.HP
+\fB\-d\fR devval1,deval2,devvalN
+.IP
+Specify a device value to sort against
+.TP
+\fB\-p\fR
+Sort device value by PCS (Lab) target
+.TP
+\fB\-f\fR [illum]
+Use Fluorescent Whitening Agent compensation [opt. simulated inst. illum.:
+.IP
+M0, M1, M2, A, C, D50 (def.), D50M2, D65, F5, F8, F10 or file.sp]
+.TP
+\fB\-i\fR illum
+Choose illuminant for computation of CIE XYZ from spectral data & FWA:
+.IP
+A, C, D50 (def.), D50M2, D65, F5, F8, F10 or file.sp
+.TP
+\fB\-o\fR observ
+Choose CIE Observer for spectral data:
+.IP
+1931_2 (def), 1964_10, S&B 1955_2, shaw, J&V 1978_2
+.TP
+\fB\-I\fR intent
+r = relative colorimetric, a = absolute (default)
+.TP
+data.ti3
+Test data file
+.TP
+iccprofile.icm
+Profile to check against
diff --git a/debian/man/refine.1 b/debian/man/refine.1
new file mode 100644
index 0000000..8c05df5
--- /dev/null
+++ b/debian/man/refine.1
@@ -0,0 +1,57 @@
+.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.44.1.
+.TH CREATE "1" "September 2014" "refine" "User Commands"
+.SH NAME
+Create \- Create abstract correction profile given table of absolute CIE correction values.
+.SH DESCRIPTION
+Create abstract correction profile given table of absolute CIE correction values, Version 1.6.3
+.SH SYNOPSIS
+.B refine
+.RB [\-options]\ cietarget\ ciecurrent\ [outdevicc]\ [inabs]\ outabs
+.TP
+\fB\-v\fR
+Verbose
+.TP
+\fB\-c\fR
+Create initial abstract correction profile
+.TP
+\fB\-g\fR
+Don't impose output device gamut limit
+.TP
+\fB\-r\fR res
+Set abstract profile clut resolution (default 33)
+.TP
+\fB\-d\fR factor
+Override default damping factor (default 0.950000, then 0.700000)
+.TP
+\fB\-R\fR
+Aim for white point relative match rather than absolute
+.TP
+\fB\-f\fR [illum]
+Use Fluorescent Whitening Agent compensation [opt. simulated inst. illum.:
+.IP
+M0, M1, M2, A, C, D50 (def.), D50M2, D65, F5, F8, F10 or file.sp]
+.TP
+\fB\-i\fR illum
+Choose illuminant for computation of CIE XYZ from spectral data & FWA:
+.IP
+A, C, D50 (def.), D50M2, D65, F5, F8, F10 or file.sp
+.TP
+\fB\-o\fR observ
+Choose CIE Observer for spectral data:
+.IP
+1931_2 (def), 1964_10, S&B 1955_2, shaw, J&V 1978_2
+.TP
+cietarget
+Target CIE or spectral values, CGATS file (e.g. .ti3)
+.TP
+ciecurrent
+Actual CIE or spectral values, CGATS file (e.g. .ti3)
+.TP
+[outdevicc]
+Output device ICC profile to set gamut limit (not used if \fB\-g\fR)
+.TP
+[inabs]
+Previous abstract correction ICC profile (not used if \fB\-c\fR)
+.TP
+outabs
+Created/refined abstract correction ICC profile
diff --git a/debian/man/revfix.1 b/debian/man/revfix.1
new file mode 100644
index 0000000..b6cc94c
--- /dev/null
+++ b/debian/man/revfix.1
@@ -0,0 +1,49 @@
+.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.44.1.
+.TH INVERT "1" "September 2014" "revfix" "User Commands"
+.SH NAME
+Invert \- Invert AtoB1 to make BtoA1 for CMYK profiles.
+.SH DESCRIPTION
+Invert AtoB1 to make BtoA1 for CMYK profiles, Version 1.6.3
+.SH SYNOPSIS
+.B revfix
+.RB [\-options]\ iccin\ iccout
+.TP
+\fB\-v\fR
+Verbose
+.TP
+\fB\-0\fR
+Process perceptual
+.TP
+\fB\-1\fR
+Process absolute/relative colorimetric
+.TP
+\fB\-2\fR
+Process saturation
+.TP
+\fB\-r\fR res
+Override BtoA1 Clut res
+.TP
+\fB\-k\fR [ezhxr]
+e = same K as existing BtoA table (def)
+z = zero, h = 0.5 K, x = max K, r = ramp K
+.HP
+\fB\-k\fR p stle stpo enle enpo shape
+.IP
+p = curve parameters
+stle: K level at White 0.0 \- 1.0
+stpo: start point of transition Wh 0.0 \- Bk 1.0
+enpo: End point of transition Wh 0.0 \- Bk 1.0
+enle: K level at Black 0.0 \- 1.0
+shape: 1.0 = straight, 0.0\-1.0 concave, 1.0\-2.0 convex
+.TP
+\fB\-K\fR parameters
+Same as \fB\-k\fR, but target is K locus rather than K value itself
+.TP
+\fB\-l\fR tlimit
+set total ink limit, 0 \- 400% (estimate by default)
+.TP
+\fB\-L\fR klimit
+set black ink limit, 0 \- 100% (estimate by default)
+.TP
+\fB\-p\fR absprof
+Include abstract profile in output tables
diff --git a/debian/man/scanin.1 b/debian/man/scanin.1
new file mode 100644
index 0000000..e1d0656
--- /dev/null
+++ b/debian/man/scanin.1
@@ -0,0 +1,114 @@
+.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.44.1.
+.TH SCANIN, "1" "September 2014" "scanin" "User Commands"
+.SH NAME
+Scanin, \- Scanin.
+.SH DESCRIPTION
+Scanin, Version 1.6.3
+.SH SYNOPSIS
+.B scanin
+.RB [options]\ input.tif\ recogin.cht\ valin.cie\ [diag.tif]
+.IP
+:\- inputs 'input.tif' and outputs scanner 'input.ti3', or
+.PP
+.B scanin
+.RB \fB\-g\fR\ [options]\ input.tif\ recogout.cht\ [diag.tif]
+.IP
+:\- outputs file 'recogout.cht', or
+.PP
+.B scanin
+.RB \fB\-o\fR\ [options]\ input.tif\ recogin.cht\ [diag.tif]
+.IP
+:\- outputs file 'input.val', or
+.PP
+.B scanin
+.RB \fB\-c\fR\ [options]\ input.tif\ recogin.cht\ scanprofile.[icc|mpp]\ pbase\ [diag.tif]
+.IP
+:\- inputs pbase.ti2 and outputs printer pbase.ti3, or
+.PP
+.B scanin
+.RB \fB\-r\fR\ [options]\ input.tif\ recogin.cht\ pbase\ [diag.tif]
+.IP
+:\- inputs pbase.ti2+.ti3 and outputs pbase.ti3
+.TP
+\fB\-g\fR
+Generate a chart reference (.cht) file
+.TP
+\fB\-o\fR
+Output patch values in .val file
+.TP
+\fB\-c\fR
+Use image to measure color to convert printer pbase .ti2 to .ti3
+.TP
+\fB\-ca\fR
+Same as \fB\-c\fR, but accumulates more values to pbase .ti3
+from subsequent pages
+.TP
+\fB\-r\fR
+Replace device values in pbase .ti2/.ti3
+Default is to create a scanner .ti3 file
+.TP
+\fB\-F\fR x1,y1,x2,y2,x3,y3,x4,y4
+Don't auto recognize, locate using four fiducual marks
+.TP
+\fB\-p\fR
+Compensate for perspective distortion
+.TP
+\fB\-a\fR
+Recognise chart in normal orientation only (\fB\-A\fR fallback as is)
+Default is to recognise all possible chart angles
+.TP
+\fB\-m\fR
+Return true mean (default is robust mean)
+.TP
+\fB\-G\fR gamma
+Approximate gamma encoding of image
+.TP
+\fB\-v\fR [n]
+Verbosity level 0\-9
+.TP
+\fB\-d\fR [ihvglLIcrsonap]
+Generate diagnostic output (try \fB\-dipn\fR)
+.TP
+i
+diag \- B&W of input image
+.TP
+h
+diag \- Horizontal edge/tick detection
+.TP
+v
+diag \- Vertical edge/tick detection
+.TP
+g
+diag \- Groups detected
+.TP
+l
+diag \- Lines detected
+.TP
+L
+diag \- All lines detected
+.TP
+I
+diag \- lines used to improve fit
+.TP
+c
+diag \- lines perspective corrected
+.TP
+r
+diag \- lines rotated
+.TP
+s
+diag \- diagnostic sample boxes rotated
+.TP
+o
+diag \- sample box outlines
+.TP
+n
+diag \- sample box names
+.TP
+a
+diag \- sample box areas
+.TP
+p
+diag \- pixel areas sampled
+.HP
+\fB\-O\fR outputfile Override the default output filename & extension.
diff --git a/debian/man/spec2cie.1 b/debian/man/spec2cie.1
new file mode 100644
index 0000000..79f23b7
--- /dev/null
+++ b/debian/man/spec2cie.1
@@ -0,0 +1,47 @@
+.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.44.1.
+.TH CONVERT "1" "September 2014" "spec2cie" "User Commands"
+.SH NAME
+Convert \- Convert spectral .ti3 file.
+.SH SYNOPSIS
+.B spec2cie
+[\fIoptions\fR] \fIinput.ti3 output.ti3\fR
+.SH DESCRIPTION
+Convert spectral .ti3 file, Version 1.6.3
+.SH SYNOPSIS
+.B spec2cie
+.RB [options]\ input.ti3\ output.ti3
+.TP
+\fB\-v\fR
+Verbose mode
+.TP
+\fB\-I\fR illum
+Override actual instrument illuminant in .ti3 file:
+.IP
+A, C, D50, D50M2, D65, F5, F8, F10 or file.sp
+(only used in conjunction with \fB\-f\fR)
+.TP
+\fB\-f\fR [illum]
+Use Fluorescent Whitening Agent compensation [simulated inst. illum.:
+.IP
+M0, M1, M2, A, C, D50 (def.), D50M2, D65, F5, F8, F10 or file.sp]
+.TP
+\fB\-i\fR illum
+Choose illuminant for computation of CIE XYZ from spectral data & FWA:
+A, C, D50 (def.), D50M2, D65, F5, F8, F10 or file.sp
+.TP
+\fB\-o\fR observ
+Choose CIE Observer for spectral data:
+.IP
+1931_2 (def), 1964_10, S&B 1955_2, shaw, J&V 1978_2
+.TP
+\fB\-n\fR
+Don't output spectral values
+.TP
+\fB\-p\fR
+Plot each values spectrum
+.TP
+input.ti3
+Measurement file
+.TP
+output.ti3
+Converted measurement file
diff --git a/debian/man/specplot.1 b/debian/man/specplot.1
new file mode 100644
index 0000000..d4426f3
--- /dev/null
+++ b/debian/man/specplot.1
@@ -0,0 +1,28 @@
+.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.44.1.
+.TH PLOT "1" "September 2014" "specplot" "User Commands"
+.SH NAME
+Plot \- Plot spectrum and calculate CCT and VCT.
+.SH DESCRIPTION
+Plot spectrum and calculate CCT and VCT
+.SH SYNOPSIS
+.B specplot
+.RB [infile.sp]
+.TP
+\fB\-v\fR
+verbose
+.TP
+\fB\-c\fR
+combine multiple files into one plot
+.TP
+\fB\-z\fR
+don't make range cover zero
+.TP
+\fB\-u\fR level
+plot effect of adding estimated UV level
+.TP
+\fB\-U\fR
+plot effect of adding range of estimated UV level
+.TP
+[infile.sp ...]
+spectrum files to plot
+default is all built in illuminants
diff --git a/debian/man/splitti3.1 b/debian/man/splitti3.1
new file mode 100644
index 0000000..4be8b4e
--- /dev/null
+++ b/debian/man/splitti3.1
@@ -0,0 +1,33 @@
+.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.44.1.
+.TH SPLIT "1" "September 2014" "splitcgats" "User Commands"
+.SH NAME
+Split \- Split a .ti3 into two.
+.SH DESCRIPTION
+Split a .ti3 into two, Version 1.6.3
+.SH SYNOPSIS
+.B splitcgats
+.RB [\-options]\ input.ti3\ output1.ti3\ output2.ti3
+.TP
+\fB\-v\fR
+Verbose \- print each patch value
+.TP
+\fB\-n\fR no
+Put no sets in first file, and balance in second file.
+.TP
+\fB\-p\fR percent
+Put percent% sets in first file, and balance in second file. (def. 50%)
+.TP
+\fB\-w\fR
+Put white patches in both files.
+.TP
+\fB\-r\fR seed
+Use given random seed.
+.TP
+input.ti3
+File to be split up.
+.TP
+output1.ti3
+First output file
+.TP
+output2.ti3
+Second output file
diff --git a/debian/man/spotread.1 b/debian/man/spotread.1
new file mode 100644
index 0000000..050f708
--- /dev/null
+++ b/debian/man/spotread.1
@@ -0,0 +1,123 @@
+.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.44.1.
+.TH MEASURE "1" "September 2014" "spotread" "User Commands"
+.SH NAME
+Measure \- Read Print Spot values.
+.SH DESCRIPTION
+Measure spot values, Version 1.6.3
+.SH SYNOPSIS
+.B spotread
+.RB [\-options]\ [logfile]
+.TP
+\fB\-v\fR
+Verbose mode
+.TP
+\fB\-s\fR
+Print spectrum for each reading
+.TP
+\fB\-S\fR
+Plot spectrum for each reading
+.TP
+\fB\-c\fR listno
+Set communication port from the following list (default 1)
+.IP
+** No ports found **
+.TP
+\fB\-t\fR
+Use transmission measurement mode
+.TP
+\fB\-e\fR
+Use emissive measurement mode (absolute results)
+.TP
+\fB\-eb\fR
+Use display white brightness relative measurement mode
+.TP
+\fB\-ew\fR
+Use display white point relative chromatically adjusted mode
+.TP
+\fB\-p\fR
+Use telephoto measurement mode (absolute results)
+.TP
+\fB\-pb\fR
+Use projector white brightness relative measurement mode
+.TP
+\fB\-pw\fR
+Use projector white point relative chromatically adjusted mode
+.TP
+\fB\-a\fR
+Use ambient measurement mode (absolute results)
+.TP
+\fB\-f\fR
+Use ambient flash measurement mode (absolute results)
+.TP
+\fB\-I\fR illum
+Set simulated instrument illumination using FWA (def \fB\-i\fR illum):
+M0, M1, M2, A, C, D50, D50M2, D65, F5, F8, F10 or file.sp]
+.TP
+\fB\-i\fR illum
+Choose illuminant for computation of CIE XYZ from spectral data & FWA:
+A, C, D50 (def.), D50M2, D65, F5, F8, F10 or file.sp
+.TP
+\fB\-Q\fR observ
+Choose CIE Observer for spectral data or CCSS instrument:
+1931_2 (def), 1964_10, S&B 1955_2, shaw, J&V 1978_2
+(Choose FWA during operation)
+.TP
+\fB\-F\fR filter
+Set filter configuration (if aplicable):
+.TP
+n
+None
+.TP
+p
+Polarising filter
+.TP
+6
+D65
+.TP
+u
+U.V. Cut
+.TP
+\fB\-E\fR extrafilterfile
+Apply extra filter compensation file
+.TP
+\fB\-x\fR
+Display Yxy instead of Lab
+.TP
+\fB\-h\fR
+Display LCh instead of Lab
+.TP
+\fB\-V\fR
+Show running average and std. devation from ref.
+.TP
+\fB\-T\fR
+Display correlated color temperatures and CRI
+.TP
+\fB\-N\fR
+Disable auto calibration of instrument
+.TP
+\fB\-H\fR
+Start in high resolution spectrum mode (if available)
+.TP
+\fB\-X\fR file.ccmx
+Apply Colorimeter Correction Matrix
+.TP
+\fB\-X\fR file.ccss
+Use Colorimeter Calibration Spectral Samples for calibration
+.TP
+\fB\-Y\fR r|n
+Override refresh, non\-refresh display mode
+.TP
+\fB\-Y\fR R:rate
+Override measured refresh rate with rate Hz
+.TP
+\fB\-Y\fR A
+Use non\-adaptive integration time mode (if available).
+.TP
+\fB\-W\fR n|h|x
+Override serial port flow control: n = none, h = HW, x = Xon/Xoff
+.TP
+\fB\-D\fR [level]
+Print debug diagnostics to stderr
+.TP
+logfile
+Optional file to save reading results as text
diff --git a/debian/man/synthcal.1 b/debian/man/synthcal.1
new file mode 100644
index 0000000..5fce9da
--- /dev/null
+++ b/debian/man/synthcal.1
@@ -0,0 +1,47 @@
+.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.44.1.
+.TH CREATE "1" "September 2014" "synthcal" "User Commands"
+.SH NAME
+Create \- Create a synthetic calibration file.
+.SH DESCRIPTION
+Create a synthetic calibration file, Version 1.6.3
+.SH SYNOPSIS
+.B synthcal
+.RB [\-options]\ outfile
+.TP
+\fB\-t\fR N
+i = input, o = output, d = display (default)
+.TP
+\fB\-d\fR col_comb
+choose colorant combination from the following (default 3):
+0: Print grey
+1: Video grey
+2: Print RGB
+3: Video RGB
+4: CMYK
+5: CMY
+6: CMYK + Light CM
+7: CMYK + Light CMK
+8: CMYK + Red + Blue
+9: CMYK + Orange + Green
+10: CMYK + Light CMK + Light Light K
+11: CMYK + Orange + Green + Light CM
+12: CMYK + Light CM + Medium CM
+.TP
+\fB\-D\fR colorant
+Add or delete colorant from combination:
+(Use \-?? to list known colorants)
+.TP
+\fB\-o\fR o1,o2,o3,
+Set non\-linear curve offset (default 0.0)
+.TP
+\fB\-s\fR s1,s2,s3,
+Set non\-linear curve scale (default 1.0)
+.TP
+\fB\-p\fR p1,p2,p3,
+Set non\-linear curve powers (default 1.0)
+.TP
+\fB\-E\fR description
+Set the profile dEscription string
+.TP
+outfile
+Base name for output .cal file
diff --git a/debian/man/synthread.1 b/debian/man/synthread.1
new file mode 100644
index 0000000..9a52806
--- /dev/null
+++ b/debian/man/synthread.1
@@ -0,0 +1,45 @@
+.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.44.1.
+.TH SYNTHETIC "1" "September 2014" "synthread" "User Commands"
+.SH NAME
+Synthetic \- Synthetic device model test chart reader.
+.SH DESCRIPTION
+Synthetic device model test chart reader \- Version 1.6.3
+.SH SYNOPSIS
+.B synthread
+.RB [\-v]\ [\-s]\ [separation.icm]\ profile.[icc|mpp|ti3]\ outfile
+.TP
+\fB\-v\fR
+Verbose mode
+.TP
+\fB\-p\fR
+Use separation profile
+.TP
+\fB\-l\fR
+Construct and output in Lab rather than XYZ
+.TP
+\fB\-i\fR p1,p2,p3,
+Set input channel curve powers (default 1.0)
+.TP
+\fB\-k\fR x1:y1,x2:y2,x3:y2
+Set input channel inflection points (default 0.5,0.5)
+.TP
+\fB\-o\fR p1,p2,p3,
+Set output channel curve powers (default 1.0)
+.TP
+\fB\-r\fR level
+Add average random deviation of <level>% to input device values (after sep.)
+.TP
+\fB\-R\fR level
+Add average random deviation of <level>% to output PCS values
+.TP
+\fB\-u\fR
+Make random deviations have uniform distributions rather than normal
+.TP
+\fB\-b\fR L,a,b
+Scale black point to target Lab value
+.TP
+[separation.icm]
+Device link separation profile
+.IP
+profile.[icc|mpp|ti3] ICC, MPP profile or TI3 to use
+outfile Base name for input[ti1]/output[ti3] file
diff --git a/debian/man/targen.1 b/debian/man/targen.1
new file mode 100644
index 0000000..6ad7229
--- /dev/null
+++ b/debian/man/targen.1
@@ -0,0 +1,112 @@
+.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.44.1.
+.TH GENERATE "1" "September 2014" "targen" "User Commands"
+.SH NAME
+Generate \- Generate Target deviceb test chart color values.
+.SH DESCRIPTION
+Generate Target deviceb test chart color values, Version 1.6.3
+.SH SYNOPSIS
+.B targen
+.RB [options]\ outfile
+.TP
+\fB\-v\fR [level]
+Verbose mode [optional level 1..N]
+.TP
+\fB\-d\fR col_comb
+choose colorant combination from the following:
+0: Print grey
+1: Video grey
+2: Print RGB
+3: Video RGB
+4: CMYK
+5: CMY
+6: CMYK + Light CM
+7: CMYK + Light CMK
+8: CMYK + Red + Blue
+9: CMYK + Orange + Green
+10: CMYK + Light CMK + Light Light K
+11: CMYK + Orange + Green + Light CM
+12: CMYK + Light CM + Medium CM
+.TP
+\fB\-D\fR colorant
+Add or delete colorant from combination:
+(Use \-?? to list known colorants)
+.TP
+\fB\-G\fR
+Generate good optimized points rather than Fast
+.TP
+\fB\-e\fR patches
+White test patches (default 4)
+.TP
+\fB\-B\fR patches
+Black test patches (default 4 Grey/RGB, else 0)
+.TP
+\fB\-s\fR steps
+Single channel steps (default grey 50, color 0)
+.TP
+\fB\-g\fR steps
+Grey axis RGB or CMY steps (default 0)
+.TP
+\fB\-m\fR steps
+Multidimensional device space cube steps (default 0)
+.TP
+\fB\-b\fR steps
+Multidimensional body centered cubic steps (default 0)
+.TP
+\fB\-f\fR patches
+Add iterative & adaptive full spread patches to total (default grey 0, color 836)
+Default is Optimised Farthest Point Sampling (OFPS)
+.TP
+\fB\-t\fR
+Use incremental far point for full spread
+.TP
+\fB\-r\fR
+Use device space random for full spread
+.TP
+\fB\-R\fR
+Use perceptual space random for full spread
+.TP
+\fB\-q\fR
+Use device space\-filling quasi\-random for full spread
+.TP
+\fB\-Q\fR
+Use perceptual space\-filling quasi\-random for full spread
+.TP
+\fB\-i\fR
+Use device space body centered cubic grid for full spread
+.TP
+\fB\-I\fR
+Use perceptual space body centered cubic grid for full spread
+.TP
+\fB\-a\fR angle
+Simplex grid angle 0.0 \- 0.5 for B.C.C. grid, default 0.333300
+.TP
+\fB\-A\fR adaptation
+Degree of adaptation of OFPS 0.0 \- 1.0 (default 0.1, \fB\-c\fR profile used 1.0)
+.TP
+\fB\-l\fR ilimit
+Total ink limit in % (default = none)
+.TP
+\fB\-p\fR power
+Optional power\-like value applied to all device values.
+.TP
+\fB\-c\fR profile
+Optional device ICC or MPP pre\-conditioning profile filename
+(Use "none" to turn off any conditioning)
+.TP
+\fB\-N\fR nemphasis
+Degree of neutral axis patch concentration 0.0\-1.0 (default 0.50)
+.TP
+\fB\-V\fR demphasis
+Degree of dark region patch concentration 1.0\-4.0 (default 1.00 = none)
+.TP
+\fB\-F\fR L,a,b,rad
+Filter out samples outside Lab sphere.
+.TP
+\fB\-w\fR
+Dump diagnostic outfilel.wrl file (Lab locations)
+.TP
+\fB\-W\fR
+Dump diagnostic outfiled.wrl file (Device locations)
+.TP
+outfile
+Base name for output(.ti1)
diff --git a/debian/man/tiffgamut.1 b/debian/man/tiffgamut.1
new file mode 100644
index 0000000..760c72f
--- /dev/null
+++ b/debian/man/tiffgamut.1
@@ -0,0 +1,90 @@
+.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.44.1.
+.TH CREATE "1" "September 2014" "tiffgamut" "User Commands"
+.SH NAME
+Create \- Create VRML image of the gamut surface of a TIFF.
+.SH DESCRIPTION
+Create VRML image of the gamut surface of a TIFF or JPEG, Version 1.6.3
+.SH SYNOPSIS
+.B tiffgamut
+.RB [\-v\ level]\ [profile.icm\ |\ embedded.tif/jpg]\ infile1.tif/jpg\ [infile2.tif/jpg\ ...]
+.TP
+\fB\-v\fR
+Verbose
+.TP
+\fB\-d\fR sres
+Surface resolution details 1.0 \- 50.0
+.TP
+\fB\-w\fR
+emit VRML .wrl file as well as CGATS .gam file
+.TP
+\fB\-n\fR
+Don't add VRML axes or white/black point
+.TP
+\fB\-k\fR
+Add markers for prim. & sec. "cusp" points
+.TP
+\fB\-f\fR perc
+Filter by popularity, perc = percent to use
+.TP
+\fB\-i\fR intent
+p = perceptual, r = relative colorimetric,
+s = saturation, a = absolute (default), d = profile default
+.TP
+\fB\-p\fR oride
+l = Lab_PCS (default), j = CIECAM02 Appearance Jab
+.TP
+\fB\-o\fR order
+n = normal (priority: lut > matrix > monochrome)
+r = reverse (priority: monochrome > matrix > lut)
+.TP
+\fB\-c\fR viewcond
+set appearance mode and viewing conditions for CIECAM02,
+either an enumerated choice, or a parameter:value changes
+.IP
+pp \- Practical Reflection Print (ISO\-3664 P2)
+pe \- Print evaluation environment (CIE 116\-1995)
+pc \- Critical print evaluation environment (ISO\-3664 P1)
+mt \- Monitor in typical work environment
+mb \- Bright monitor in bright work environment
+md \- Monitor in darkened work environment
+jm \- Projector in dim environment
+jd \- Projector in dark environment
+tv \- Television/Film Studio
+.IP
+pcd \- Photo CD \- original scene outdoors
+.IP
+ob \- Original scene \- Bright Outdoors
+cx \- Cut Sheet Transparencies on a viewing box
+.TP
+s:surround
+n = auto, a = average, m = dim, d = dark,
+c = transparency (default average)
+.TP
+w:X:Y:Z
+Adapted white point as XYZ (default media white)
+.TP
+w:x:y
+Adapted white point as x, y
+.TP
+a:adaptation
+Adaptation luminance in cd.m^2 (default 50.0)
+.TP
+b:background
+Background % of image luminance (default 20)
+.TP
+l:imagewhite
+Image white in cd.m^2 if surround = auto (default 250)
+.TP
+f:flare
+Flare light % of image luminance (default 0)
+.TP
+g:glare
+Flare light % of ambient (default 1)
+.TP
+g:X:Y:Z
+Flare color as XYZ (default media white, Abs: D50)
+.TP
+g:x:y
+Flare color as x, y
+.HP
+\fB\-O\fR outputfile Override the default output filename.
diff --git a/debian/man/timage.1 b/debian/man/timage.1
new file mode 100644
index 0000000..2ac441a
--- /dev/null
+++ b/debian/man/timage.1
@@ -0,0 +1,33 @@
+.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.44.1.
+.TH CREATE "1" "September 2014" "timage" "User Commands"
+.SH NAME
+Create \- Create test images, default hex RGB surface and wedge.
+.SH DESCRIPTION
+Create test images, default hex RGB surface and wedge, Version 1.6.3
+.SH SYNOPSIS
+.B timage
+.RB [\-options]\ outfile.tif
+.TP
+\fB\-t\fR
+Generate rectangular gamut boundary test chart
+.TP
+\fB\-p\fR steps
+Generate a colorspace step chart with L* steps^2
+.TP
+\fB\-r\fR res
+Resolution in DPI (default 200)
+.TP
+\fB\-s\fR
+Smooth blend
+.TP
+\fB\-x\fR
+16 bit output
+.TP
+\fB\-4\fR
+CMYK output
+.TP
+\fB\-g\fR prop
+Percentage towards grey (default 0%)
+.TP
+outfile.tif
+Profile to check against
diff --git a/debian/man/txt2ti3.1 b/debian/man/txt2ti3.1
new file mode 100644
index 0000000..f0c44c4
--- /dev/null
+++ b/debian/man/txt2ti3.1
@@ -0,0 +1,33 @@
+.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.44.1.
+.TH CONVERT "1" "September 2014" "txt2ti3" "User Commands"
+.SH NAME
+Convert \- Convert Gretag/Logo or X-Rite ColorPport raw RGB or CMYK device profile data to Argyll CGATS data.
+.SH DESCRIPTION
+Convert Gretag/Logo or X\-Rite ColorPport raw RGB or CMYK device profile data to Argyll CGATS data, Version 1.6.3
+.SH SYNOPSIS
+.B txt2ti3
+.RB [\-v]\ [\-l\ limit]\ [devfile]\ infile\ [specfile]\ outbase
+.TP
+\fB\-2\fR
+Create dummy .ti2 file as well
+.TP
+\fB\-l\fR limit
+set ink limit, 0 \- 400% (default max in file)
+.TP
+\fB\-d\fR
+Set type of device as Display, not Output
+.TP
+\fB\-i\fR
+Set type of device as Input, not Output
+.TP
+[devfile]
+Input Device CMYK target file (typically file.txt)
+.TP
+infile
+Input CIE, Spectral or Device & Spectral file (typically file.txt)
+.TP
+[specfile]
+Input Spectral file (typically file.txt)
+.TP
+outbasename
+Output file basename for .ti3 and .ti2
diff --git a/debian/man/viewgam.1 b/debian/man/viewgam.1
new file mode 100644
index 0000000..83afd63
--- /dev/null
+++ b/debian/man/viewgam.1
@@ -0,0 +1,42 @@
+.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.44.1.
+.TH VIEW "1" "September 2014" "viewgam" "User Commands"
+.SH NAME
+View \- View gamuts.
+.SH DESCRIPTION
+View gamuts Version 1.6.3
+.SH SYNOPSIS
+.B viewgam
+.RB { [\-c\ color]\ [\-t\ trans]\ [\-w|s]\ infile.gam }\ ...\ outfile.wrl
+.TP
+\fB\-c\fR color
+Color to make gamut, r = red, g = green, b = blue
+c = cyan, m = magenta, y = yellow, e = grey, w = white
+n = natural color
+.TP
+\fB\-t\fR trans
+Set transparency from 0.0 (opaque) to 1.0 (invisible)
+.TP
+\fB\-w\fR
+Show as a wireframe
+.TP
+\fB\-s\fR
+Show as a solid surace
+.TP
+infile.gam
+Name of .gam file
+Repeat above for each input file
+.TP
+\fB\-n\fR
+Don't add Lab axes
+.TP
+\fB\-k\fR
+Add markers for prim. & sec. "cusp" points
+.TP
+\fB\-i\fR
+Compute and print intersecting volume of first 2 gamuts
+.TP
+\fB\-I\fR isect.gam
+Same as \fB\-i\fR, but save intersection gamut to isect.gam
+.TP
+outfile.wrl
+Name of output .wrl file
diff --git a/debian/man/xicclu.1 b/debian/man/xicclu.1
new file mode 100644
index 0000000..b5aa7e9
--- /dev/null
+++ b/debian/man/xicclu.1
@@ -0,0 +1,162 @@
+.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.44.1.
+.TH LOOKUP "1" "September 2014" "xicclu" "User Commands"
+.SH NAME
+Lookup \- Translate colors through an xicc.
+.SH DESCRIPTION
+Lookup ICC or CAL colors, Version 1.6.3
+.SH SYNOPSIS
+.B xicclu
+.RB [\-options]\ profile_or_cal
+.TP
+\fB\-v\fR level
+Verbosity level 0 \- 2 (default = 1)
+.TP
+\fB\-g\fR
+Plot slice instead of looking colors up. (Default white to black)
+.TP
+\fB\-G\fR s:L:a:b
+Override plot slice start with Lab or Jab co\-ordinate
+.TP
+\fB\-G\fR e:L:a:b
+Override plot slice end with Lab or Jab co\-ordinate
+.TP
+\fB\-f\fR function
+f = forward, b = backwards, g = gamut, p = preview
+if = inverted forward, ib = inverted backwards
+.TP
+\fB\-i\fR intent
+a = absolute, r = relative colorimetric
+p = perceptual, s = saturation
+.TP
+\fB\-o\fR order
+n = normal (priority: lut > matrix > monochrome)
+r = reverse (priority: monochrome > matrix > lut)
+.TP
+\fB\-p\fR oride
+x = XYZ_PCS, X = XYZ * 100, l = Lab_PCS, L = LCh, y = Yxy
+j = CIECAM02 Appearance Jab, J = CIECAM02 Appearance JCh
+.TP
+\fB\-s\fR scale
+Scale device range 0.0 \- scale rather than 0.0 \- 1.0
+.TP
+\fB\-e\fR flag
+Video encode device input as:
+.TP
+\fB\-E\fR flag
+Video decode device output as:
+.TP
+n
+normal 0..1 full range RGB levels (default)
+.TP
+t
+(16\-235)/255 "TV" RGB levels
+.TP
+6
+Rec601 YCbCr SD (16\-235,240)/255 "TV" levels
+.TP
+7
+Rec709 1125/60Hz YCbCr HD (16\-235,240)/255 "TV" levels
+.TP
+5
+Rec709 1250/50Hz YCbCr HD (16\-235,240)/255 "TV" levels
+.TP
+2
+Rec2020 YCbCr UHD (16\-235,240)/255 "TV" levels
+.TP
+C
+Rec2020 Constant Luminance YCbCr UHD (16\-235,240)/255 "TV" levels
+.TP
+\fB\-k\fR [zhxrlv]
+Black value target: z = zero K,
+h = 0.5 K, x = max K, r = ramp K (def.)
+l = extra PCS input is portion of K locus
+v = extra PCS input is K target value
+.HP
+\fB\-k\fR p stle stpo enpo enle shape
+.IP
+stle: K level at White 0.0 \- 1.0
+stpo: start point of transition Wh 0.0 \- Bk 1.0
+enpo: End point of transition Wh 0.0 \- Bk 1.0
+enle: K level at Black 0.0 \- 1.0
+shape: 1.0 = straight, 0.0\-1.0 concave, 1.0\-2.0 convex
+.HP
+\fB\-k\fR q stle0 stpo0 enpo0 enle0 shape0 stle2 stpo2 enpo2 enle2 shape2
+.IP
+Transfer extra PCS input to dual curve limits
+.TP
+\fB\-K\fR parameters
+Same as \fB\-k\fR, but target is K locus rather than K value itself
+.TP
+\fB\-l\fR tlimit
+set total ink limit, 0 \- 400% (estimate by default)
+.TP
+\fB\-L\fR klimit
+set black ink limit, 0 \- 100% (estimate by default)
+.TP
+\fB\-a\fR
+show actual target values if clipped
+.TP
+\fB\-u\fR
+warn if output PCS is outside the spectrum locus
+.TP
+\fB\-m\fR
+merge output processing into clut
+.TP
+\fB\-b\fR
+use CAM Jab for clipping
+.TP
+\fB\-c\fR viewcond
+set viewing conditions for CIECAM97s,
+either an enumerated choice, or a parameter:value changes
+.IP
+pp \- Practical Reflection Print (ISO\-3664 P2)
+pe \- Print evaluation environment (CIE 116\-1995)
+pc \- Critical print evaluation environment (ISO\-3664 P1)
+mt \- Monitor in typical work environment
+mb \- Bright monitor in bright work environment
+md \- Monitor in darkened work environment
+jm \- Projector in dim environment
+jd \- Projector in dark environment
+tv \- Television/Film Studio
+.IP
+pcd \- Photo CD \- original scene outdoors
+.IP
+ob \- Original scene \- Bright Outdoors
+cx \- Cut Sheet Transparencies on a viewing box
+.TP
+s:surround
+n = auto, a = average, m = dim, d = dark,
+c = transparency (default average)
+.TP
+w:X:Y:Z
+Adapted white point as XYZ (default media white, Abs: D50)
+.TP
+w:x:y
+Adapted white point as x, y
+.TP
+a:adaptation
+Adaptation luminance in cd.m^2 (default 50.0)
+.TP
+b:background
+Background % of image luminance (default 20)
+.TP
+l:imagewhite
+Image white in cd.m^2 if surround = auto (default 250)
+.TP
+f:flare
+Flare light % of image luminance (default 0)
+.TP
+g:glare
+Flare light % of ambient (default 1)
+.TP
+g:X:Y:Z
+Flare color as XYZ (default media white, Abs: D50)
+.TP
+g:x:y
+Flare color as x, y
+.IP
+The colors to be translated should be fed into standard in,
+one input color per line, white space separated.
+A line starting with a # will be ignored.
+A line not starting with a number will terminate the program.
+Use \fB\-v0\fR for just output colors.
diff --git a/debian/missing-sources/deep_arrays.json b/debian/missing-sources/deep_arrays.json
new file mode 100644
index 0000000..3e1c094
--- /dev/null
+++ b/debian/missing-sources/deep_arrays.json
@@ -0,0 +1,18 @@
+[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[
+[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[
+[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[
+[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[
+[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[
+[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[
+[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[
+[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[
+[[[[[[[[[[[[[[[[
+]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]
+]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]
+]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]
+]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]
+]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]
+]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]
+]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]
+]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]
+]]]]]]]]]]]]]]]] \ No newline at end of file
diff --git a/debian/missing-sources/difficult_json_c_test_case.json b/debian/missing-sources/difficult_json_c_test_case.json
new file mode 100644
index 0000000..d2bf5ea
--- /dev/null
+++ b/debian/missing-sources/difficult_json_c_test_case.json
@@ -0,0 +1,7 @@
+{ "glossary":
+ { "title": "example glossary", "GlossDiv":
+ { "title": "S", "GlossList":
+ [{ "ID": "SGML", "SortAs": "SGML", "GlossTerm": "Standard Generalized Markup Language", "Acronym": "SGML", "Abbrev": "ISO 8879:1986", "GlossDef": "A meta-markup language, used to create markup languages such as DocBook.", "GlossSeeAlso": ["GML", "XML", "markup"] } ]
+ }
+ }
+}
diff --git a/debian/missing-sources/difficult_json_c_test_case_with_comments.json b/debian/missing-sources/difficult_json_c_test_case_with_comments.json
new file mode 100644
index 0000000..189dca8
--- /dev/null
+++ b/debian/missing-sources/difficult_json_c_test_case_with_comments.json
@@ -0,0 +1,7 @@
+{ "glossary":
+ { /* you */ "title": /**/ "example glossary", /*should*/"GlossDiv":
+ { "title": /*never*/"S", /*ever*/"GlossList":
+ [ { "ID": "SGML", "SortAs": "SGML", "GlossTerm": "Standard Generalized Markup Language", "Acronym": "SGML", "Abbrev": "ISO 8879:1986", "GlossDef": "A meta-markup language, used to create markup languages such as DocBook.", /*see*/"GlossSeeAlso"/*this*/:/*coming*/[/*out*/"GML"/*of*/,/*the*/"XML"/*parser!*/, "markup"] /*hey*/}/*ho*/]/*hey*/
+ }/*ho*/
+ }
+} // and the parser won't even get this far, so chill. /* hah!
diff --git a/debian/missing-sources/non_utf8_char_in_string.json b/debian/missing-sources/non_utf8_char_in_string.json
new file mode 100644
index 0000000..7d75519
--- /dev/null
+++ b/debian/missing-sources/non_utf8_char_in_string.json
@@ -0,0 +1,88 @@
+{
+ "CoreletAPIVersion":2,
+ "CoreletType":"standalone",
+ "documentation":"A corelet that provides the capability to upload a folder’s contents into a user’s locker.",
+ "functions":
+ [
+ {
+ "documentation":"Displays a dialog box that allows user to select a folder on the local system.",
+ "name":"ShowBrowseDialog","parameters":
+ [
+ {
+ "documentation":"The callback function for results.",
+ "name":"callback",
+ "required":true,
+ "type":"callback"
+ }
+ ]
+ },
+ {
+ "documentation":"Uploads all mp3 files in the folder provided.",
+ "name":"UploadFolder","parameters":
+ [
+ {
+ "documentation":"The path to upload mp3 files from.",
+ "name":"path",
+ "required":true,
+ "type":"string"
+ },
+ {
+ "documentation":"The callback function for progress.",
+ "name":"callback",
+ "required":true,
+ "type":"callback"
+ }
+ ]
+ },
+ {
+ "documentation":"Returns the server name to the current locker service.",
+ "name":"GetLockerService",
+ "parameters":[]
+ },
+ {
+ "documentation":"Changes the name of the locker service.",
+ "name":"SetLockerService",
+ "parameters":
+ [
+ {
+ "documentation":"The value of the locker service to set active.",
+ "name":"LockerService",
+ "required":true,
+ "type":"string"
+ }
+ ]
+ },
+ {
+ "documentation":"Downloads locker files to the suggested folder.",
+ "name":"DownloadFile",
+ "parameters":
+ [
+ {
+ "documentation":"The origin path of the locker file.",
+ "name":"path",
+ "required":true,
+ "type":"string"
+ },
+ {
+ "documentation":"The Window destination path of the locker file.",
+ "name":"destination",
+ "required":true,"type":"integer"
+ },
+ {
+ "documentation":"The callback function for progress.",
+ "name":"callback",
+ "required":true,
+ "type":"callback"
+ }
+ ]
+ }
+ ],
+ "name":"LockerUploader",
+ "version":
+ {
+ "major":0,
+ "micro":1,
+ "minor":0
+ },
+ "versionString":"0.0.1"
+} \ No newline at end of file
diff --git a/debian/missing-sources/x3dom.js b/debian/missing-sources/x3dom.js
new file mode 100644
index 0000000..0703be4
--- /dev/null
+++ b/debian/missing-sources/x3dom.js
@@ -0,0 +1,53578 @@
+/** X3DOM Runtime, http://www.x3dom.org/ 1.6.2 - 8f5655cec1951042e852ee9def292c9e0194186b - Sat Dec 20 00:03:52 2014 +0100 *//*
+ * X3DOM JavaScript Library
+ * http://www.x3dom.org
+ *
+ * (C)2009 Fraunhofer IGD, Darmstadt, Germany
+ * Dual licensed under the MIT and GPL
+ *
+ * Based on code originally provided by
+ * Philip Taylor: http://philip.html5.org
+ */
+
+// Add some JS1.6 Array functions:
+// (This only includes the non-prototype versions, because otherwise it messes up 'for in' loops)
+
+if (!Array.forEach) {
+ /*
+ * Function: Array.forEach
+ *
+ * Javascript array forEach() method calls a function for each element in the array.
+ *
+ * Parameters:
+ *
+ * array - The array
+ * fun - Function to test each element of the array
+ * thisp - Object to use as __this__ when executing callback
+ *
+ * Returns:
+ *
+ * The created array
+ */
+ Array.forEach = function (array, fun, thisp) {
+ var len = array.length;
+ for (var i = 0; i < len; i++) {
+ if (i in array) {
+ fun.call(thisp, array[i], i, array);
+ }
+ }
+ };
+}
+
+if (!Array.map) {
+ Array.map = function(array, fun, thisp) {
+ var len = array.length;
+ var res = [];
+ for (var i = 0; i < len; i++) {
+ if (i in array) {
+ res[i] = fun.call(thisp, array[i], i, array);
+ }
+ }
+ return res;
+ };
+}
+
+if (!Array.filter) {
+ Array.filter = function(array, fun, thisp) {
+ var len = array.length;
+ var res = [];
+ for (var i = 0; i < len; i++) {
+ if (i in array) {
+ var val = array[i];
+ if (fun.call(thisp, val, i, array)) {
+ res.push(val);
+ }
+ }
+ }
+ return res;
+ };
+}
+
+/*
+ * X3DOM JavaScript Library
+ * http://www.x3dom.org
+ *
+ * (C)2009 Fraunhofer IGD, Darmstadt, Germany
+ * Dual licensed under the MIT and GPL
+ *
+ * Based on code originally provided by
+ * Philip Taylor: http://philip.html5.org
+ */
+
+/**
+ * The Namespace container for x3dom objects.
+ * @namespace x3dom
+ * */
+var x3dom = {
+ canvases : [],
+
+ x3dNS : 'http://www.web3d.org/specifications/x3d-namespace',
+ x3dextNS : 'http://philip.html5.org/x3d/ext',
+ xsltNS : 'http://www.w3.org/1999/XSL/x3dom.Transform',
+ xhtmlNS : 'http://www.w3.org/1999/xhtml'
+};
+
+/**
+ * The x3dom.nodeTypes namespace.
+ * @namespace x3dom.nodeTypes
+ * */
+x3dom.nodeTypes = {};
+
+/**
+ * The x3dom.nodeTypesLC namespace. Stores nodetypes in lowercase
+ * @namespace x3dom.nodeTypesLC
+ * */
+x3dom.nodeTypesLC = {};
+
+/**
+ * The x3dom.components namespace.
+ * @namespace x3dom.components
+ * */
+x3dom.components = {};
+
+/** Cache for primitive nodes (Box, Sphere, etc.) */
+x3dom.geoCache = [];
+
+/** Stores information about Browser and hardware capabilities */
+x3dom.caps = { PLATFORM: navigator.platform, AGENT: navigator.userAgent, RENDERMODE: "HARDWARE" };
+
+/** Registers the node defined by @p nodeDef.
+
+ The node is registered with the given @p nodeTypeName and @p componentName.
+
+ @param nodeTypeName the name of the node type (e.g. Material, Shape, ...)
+ @param componentName the name of the component the node type belongs to
+ @param nodeDef the definition of the node type
+ */
+x3dom.registerNodeType = function(nodeTypeName, componentName, nodeDef) {
+ //console.log("Registering nodetype [" + nodeTypeName + "] in component [" + componentName + "]");
+ if (x3dom.components[componentName] === undefined) {
+ x3dom.components[componentName] = {};
+ }
+ nodeDef._typeName = nodeTypeName;
+ nodeDef._compName = componentName;
+ x3dom.components[componentName][nodeTypeName] = nodeDef;
+ x3dom.nodeTypes[nodeTypeName] = nodeDef;
+ x3dom.nodeTypesLC[nodeTypeName.toLowerCase()] = nodeDef;
+};
+
+/** Test if node is registered X3D element */
+x3dom.isX3DElement = function(node) {
+ // x3dom.debug.logInfo("node=" + node + "node.nodeType=" + node.nodeType + ", node.localName=" + node.localName + ", ");
+ var name = (node.nodeType === Node.ELEMENT_NODE && node.localName) ? node.localName.toLowerCase() : null;
+ return (name && (x3dom.nodeTypes[node.localName] || x3dom.nodeTypesLC[name] ||
+ name == "x3d" || name == "websg" || name == "route"));
+};
+
+/*
+ * Function: x3dom.extend
+ *
+ * Returns a prototype object suitable for extending the given class
+ * _f_. Rather than constructing a new instance of _f_ to serve as
+ * the prototype (which unnecessarily runs the constructor on the created
+ * prototype object, potentially polluting it), an anonymous function is
+ * generated internally that shares the same prototype:
+ *
+ * Parameters:
+ * f - Method f a constructor
+ *
+ * Returns:
+ * A suitable prototype object
+ *
+ * See Also:
+ * Douglas Crockford's essay on <prototypical inheritance at http://javascript.crockford.com/prototypal.html>.
+ */
+// TODO; unify with defineClass, which does basically the same
+x3dom.extend = function(f) {
+ function G() {}
+ G.prototype = f.prototype || f;
+ return new G();
+};
+
+/**
+ * Function x3dom.getStyle
+ *
+ * Computes the value of the specified CSS property <tt>p</tt> on the
+ * specified element <tt>e</tt>.
+ *
+ * Parameters:
+ * oElm - The element on which to compute the CSS property
+ * strCssRule - The name of the CSS property
+ *
+ * Returns:
+ *
+ * The computed value of the CSS property
+ */
+x3dom.getStyle = function(oElm, strCssRule) {
+ var strValue = "";
+ var style = document.defaultView.getComputedStyle ? document.defaultView.getComputedStyle(oElm, null) : null;
+ if (style) {
+ strValue = style.getPropertyValue(strCssRule);
+ }
+ else if(oElm.currentStyle){
+ strCssRule = strCssRule.replace(/\-(\w)/g, function (strMatch, p1){ return p1.toUpperCase(); });
+ strValue = oElm.currentStyle[strCssRule];
+ }
+ return strValue;
+};
+
+
+/** Utility function for defining a new class.
+
+ @param parent the parent class of the new class
+ @param ctor the constructor of the new class
+ @param methods an object literal containing the methods of the new class
+ @return the constructor function of the new class
+ */
+function defineClass(parent, ctor, methods) {
+ if (parent) {
+ function Inheritance() {}
+ Inheritance.prototype = parent.prototype;
+
+ ctor.prototype = new Inheritance();
+ ctor.prototype.constructor = ctor;
+ ctor.superClass = parent;
+ }
+ if (methods) {
+ for (var m in methods) {
+ ctor.prototype[m] = methods[m];
+ }
+ }
+ return ctor;
+}
+
+/** Utility function for testing a node type.
+
+ @param object the object to test
+ @param clazz the type of the class
+ @return true or false
+ */
+x3dom.isa = function(object, clazz) {
+ /*
+ if (!object || !object.constructor || object.constructor.superClass === undefined) {
+ return false;
+ }
+ if (object.constructor === clazz) {
+ return true;
+ }
+
+ function f(c) {
+ if (c === clazz) {
+ return true;
+ }
+ if (c.prototype && c.prototype.constructor && c.prototype.constructor.superClass) {
+ return f(c.prototype.constructor.superClass);
+ }
+ return false;
+ }
+ return f(object.constructor.superClass);
+ */
+ return (object instanceof clazz);
+};
+
+
+/// helper
+x3dom.getGlobal = function () {
+ return (function () {
+ return this;
+ }).call(null);
+};
+
+
+/**
+ * Load javascript file either by performing an synchronous jax request
+ * an eval'ing the response or by dynamically creating a <script> tag.
+ *
+ * CAUTION: This function is a possible source for Cross-Site
+ * Scripting Attacks.
+ *
+ * @param src The location of the source file relative to
+ * path_prefix. If path_prefix is omitted, the
+ * current directory (relative to the HTML document)
+ * is used instead.
+ * @param path_prefix A prefix URI to add to the resource to be loaded.
+ * The URI must be given in normalized path form ending in a
+ * path separator (i.e. src/nodes/). It can be in absolute
+ * URI form (http://somedomain.tld/src/nodes/)
+ * @param blocking By default the lookup is done via blocking jax request.
+ * set to false to use the script i
+ */
+x3dom.loadJS = function(src, path_prefix, blocking) {
+ blocking = (blocking === false) ? blocking : true; // default to true
+
+ if (blocking) {
+ var url = (path_prefix) ? path_prefix.trim() + src : src;
+ var req = new XMLHttpRequest();
+
+ if (req) {
+ // third parameter false = synchronous/blocking call
+ // need this to load the JS before onload completes
+ req.open("GET", url, false);
+ req.send(null); // blocking
+
+ // maybe consider global eval
+ // http://perfectionkills.com/global-eval-what-are-the-options/#indirect_eval_call_examples
+ eval(req.responseText);
+ }
+ } else {
+ var head = document.getElementsByTagName('HEAD').item(0);
+ var script = document.createElement("script");
+ var loadpath = (path_prefix) ? path_prefix.trim() + src : src;
+ if (head) {
+ x3dom.debug.logError("Trying to load external JS file: " + loadpath);
+ //alert("Trying to load external JS file: " + loadpath);
+ script.type = "text/javascript";
+ script.src = loadpath;
+ head.appendChild(script);
+ } else {
+ alert("No document object found. Can't load components!");
+ //x3dom.debug.logError("No document object found. Can't load components");
+ }
+ }
+};
+
+// helper
+function array_to_object(a) {
+ var o = {};
+ for(var i=0;i<a.length;i++) {
+ o[a[i]]='';
+ }
+ return o;
+}
+
+/**
+ * Provides requestAnimationFrame in a cross browser way.
+ * https://cvs.khronos.org/svn/repos/registry/trunk/public/webgl/sdk/demos/common/webgl-utils.js
+ */
+window.requestAnimFrame = (function() {
+ return window.requestAnimationFrame ||
+ window.webkitRequestAnimationFrame ||
+ window.mozRequestAnimationFrame ||
+ window.oRequestAnimationFrame ||
+ window.msRequestAnimationFrame ||
+ function(/* function FrameRequestCallback */ callback, /* DOMElement Element */ element) {
+ window.setTimeout(callback, 16);
+ };
+})();
+
+/**
+ * Toggle full-screen mode
+ */
+x3dom.toggleFullScreen = function() {
+ if (document.fullScreen || document.mozFullScreen || document.webkitIsFullScreen) {
+ if (document.cancelFullScreen) {
+ document.cancelFullScreen();
+ }
+ else if (document.mozCancelFullScreen) {
+ document.mozCancelFullScreen();
+ }
+ else if (document.webkitCancelFullScreen) {
+ document.webkitCancelFullScreen();
+ }
+ }
+ else {
+ var docElem = document.documentElement;
+ if (docElem.requestFullScreen) {
+ docElem.requestFullScreen();
+ }
+ else if (docElem.mozRequestFullScreen) {
+ docElem.mozRequestFullScreen();
+ }
+ else if (docElem.webkitRequestFullScreen) {
+ docElem.webkitRequestFullScreen();
+ }
+ }
+};
+
+/*
+ * X3DOM JavaScript Library
+ * http://www.x3dom.org
+ *
+ * (C)2009 Fraunhofer IGD, Darmstadt, Germany
+ * Dual licensed under the MIT and GPL
+ *
+ * Based on code originally provided by
+ * Philip Taylor: http://philip.html5.org
+ */
+
+x3dom.debug = {
+
+ INFO: "INFO",
+ WARNING: "WARNING",
+ ERROR: "ERROR",
+ EXCEPTION: "EXCEPTION",
+
+ // determines whether debugging/logging is active. If set to "false"
+ // no debugging messages will be logged.
+ isActive: false,
+
+ // stores if firebug is available
+ isFirebugAvailable: false,
+
+ // stores if the x3dom.debug object is initialized already
+ isSetup: false,
+
+ // stores if x3dom.debug object is append already (Need for IE integration)
+ isAppend: false,
+
+ // stores the number of lines logged
+ numLinesLogged: 0,
+
+ // the maximum number of lines to log in order to prevent
+ // the browser to slow down
+ maxLinesToLog: 10000,
+
+ // the container div for the logging messages
+ logContainer: null,
+
+ /** @brief Setup the x3dom.debug object.
+
+ Checks for firebug and creates the container div for the logging
+ messages.
+ */
+ setup: function() {
+ // If debugging is already setup simply return
+ if (x3dom.debug.isSetup) { return; }
+
+ // Check for firebug console
+ try {
+ if (window.console.firebug !== undefined) {
+ x3dom.debug.isFirebugAvailable = true;
+ }
+ }
+ catch (err) {
+ x3dom.debug.isFirebugAvailable = false;
+ }
+
+ //
+ x3dom.debug.setupLogContainer();
+
+ // setup should be setup only once, thus store if we done that already
+ x3dom.debug.isSetup = true;
+ },
+
+ /** @brief Activates the log
+ */
+ activate: function(visible) {
+ x3dom.debug.isActive = true;
+
+ //var aDiv = document.createElement("div");
+ //aDiv.style.clear = "both";
+ //aDiv.appendChild(document.createTextNode("\r\n"));
+ //aDiv.style.display = (visible) ? "block" : "none";
+ x3dom.debug.logContainer.style.display = (visible) ? "block" : "none";
+
+ //Need this HACK for IE/Flash integration. IE don't have a document.body at this time when starting Flash-Backend
+ if(!x3dom.debug.isAppend) {
+ if(navigator.appName == "Microsoft Internet Explorer") {
+ //document.documentElement.appendChild(aDiv);
+ x3dom.debug.logContainer.style.marginLeft = "8px";
+ document.documentElement.appendChild(x3dom.debug.logContainer);
+ }else{
+ //document.body.appendChild(aDiv);
+ document.body.appendChild(x3dom.debug.logContainer);
+ }
+ x3dom.debug.isAppend = true;
+ }
+ },
+
+ /** @brief Inserts a container div for the logging messages into the HTML page
+ */
+ setupLogContainer: function() {
+ x3dom.debug.logContainer = document.createElement("div");
+ x3dom.debug.logContainer.id = "x3dom_logdiv";
+ x3dom.debug.logContainer.setAttribute("class", "x3dom-logContainer");
+ x3dom.debug.logContainer.style.clear = "both";
+ //document.body.appendChild(x3dom.debug.logContainer);
+ },
+
+ /** @brief Generic logging function which does all the work.
+
+ @param msg the log message
+ @param logType the type of the log message. One of INFO, WARNING, ERROR
+ or EXCEPTION.
+ */
+ doLog: function(msg, logType) {
+
+ // If logging is deactivated do nothing and simply return
+ if (!x3dom.debug.isActive) { return; }
+
+ // If we have reached the maximum number of logged lines output
+ // a warning message
+ if (x3dom.debug.numLinesLogged === x3dom.debug.maxLinesToLog) {
+ msg = "Maximum number of log lines (=" + x3dom.debug.maxLinesToLog +
+ ") reached. Deactivating logging...";
+ }
+
+ // If the maximum number of log lines is exceeded do not log anything
+ // but simply return
+ if (x3dom.debug.numLinesLogged > x3dom.debug.maxLinesToLog) { return; }
+
+ // Output a log line to the HTML page
+ var node = document.createElement("p");
+ node.style.margin = 0;
+ switch (logType) {
+ case x3dom.debug.INFO:
+ node.style.color = "#00ff00";
+ break;
+ case x3dom.debug.WARNING:
+ node.style.color = "#cd853f";
+ break;
+ case x3dom.debug.ERROR:
+ node.style.color = "#ff4500";
+ break;
+ case x3dom.debug.EXCEPTION:
+ node.style.color = "#ffff00";
+ break;
+ default:
+ node.style.color = "#00ff00";
+ break;
+ }
+
+ // not sure if try/catch solves problem http://sourceforge.net/apps/trac/x3dom/ticket/52
+ // but due to no avail of ATI gfxcard can't test
+ try {
+ node.innerHTML = logType + ": " + msg;
+ x3dom.debug.logContainer.insertBefore(node, x3dom.debug.logContainer.firstChild);
+ } catch (err) {
+ if (window.console.firebug !== undefined) {
+ window.console.warn(msg);
+ }
+ }
+
+ // Use firebug's console if available
+ if (x3dom.debug.isFirebugAvailable) {
+ switch (logType) {
+ case x3dom.debug.INFO:
+ window.console.info(msg);
+ break;
+ case x3dom.debug.WARNING:
+ window.console.warn(msg);
+ break;
+ case x3dom.debug.ERROR:
+ window.console.error(msg);
+ break;
+ case x3dom.debug.EXCEPTION:
+ window.console.debug(msg);
+ break;
+ default:
+ break;
+ }
+ }
+
+ x3dom.debug.numLinesLogged++;
+ },
+
+ /** Log an info message. */
+ logInfo: function(msg) {
+ x3dom.debug.doLog(msg, x3dom.debug.INFO);
+ },
+
+ /** Log a warning message. */
+ logWarning: function(msg) {
+ x3dom.debug.doLog(msg, x3dom.debug.WARNING);
+ },
+
+ /** Log an error message. */
+ logError: function(msg) {
+ x3dom.debug.doLog(msg, x3dom.debug.ERROR);
+ },
+
+ /** Log an exception message. */
+ logException: function(msg) {
+ x3dom.debug.doLog(msg, x3dom.debug.EXCEPTION);
+ },
+
+ /** Log an assertion. */
+ assert: function(c, msg) {
+ if (!c) {
+ x3dom.debug.doLog("Assertion failed in " +
+ x3dom.debug.assert.caller.name + ': ' +
+ msg, x3dom.debug.ERROR);
+ }
+ },
+
+ /**
+ Checks the type of a given object.
+
+ @param obj the object to check.
+ @returns one of; "boolean", "number", "string", "object",
+ "function", or "null".
+ */
+ typeOf: function (obj) {
+ var type = typeof obj;
+ return type === "object" && !obj ? "null" : type;
+ },
+
+ /**
+ Checks if a property of a specified object has the given type.
+
+ @param obj the object to check.
+ @param name the property name.
+ @param type the property type (optional, default is "function").
+ @returns true if the property exists and has the specified type,
+ otherwise false.
+ */
+ exists: function (obj, name, type) {
+ type = type || "function";
+ return (obj ? this.typeOf(obj[name]) : "null") === type;
+ },
+
+ /**
+ Dumps all members of the given object.
+ */
+ dumpFields: function (node) {
+ var str = "";
+ for (var fName in node) {
+ str += (fName + ", ");
+ }
+ str += '\n';
+ x3dom.debug.logInfo(str);
+ return str;
+ }
+};
+
+// Call the setup function to... umm, well, setup x3dom.debug
+x3dom.debug.setup();
+
+/*
+ * X3DOM JavaScript Library
+ * http://www.x3dom.org
+ *
+ * (C)2009 Fraunhofer IGD, Darmstadt, Germany
+ * Dual licensed under the MIT and GPL
+ *
+ * Based on code originally provided by
+ * Philip Taylor: http://philip.html5.org
+ */
+
+//---------------------------------------------------------------------------------------------------------------------
+
+x3dom.arc = {};
+x3dom.arc.instance = null;
+
+x3dom.arc.Limits = function(min, max, initial)
+{
+ this._min = min;
+ this._max = max;
+
+ this.getValue = function(value)
+ {
+ value = this._min + (this._max - this._min) * value;
+ return this._max >= value ? (this._min <= value ? value : this._min ) : this._max;
+ };
+};
+
+//---------------------------------------------------------------------------------------------------------------------
+
+x3dom.arc.ARF = function(name, min, max, dirFac, factorGetterFunc, factorSetterFunc, getterFunc, setterFunc)
+{
+ this._name = name;
+ //start with average
+ this._stateValue = [ 0.5, 0.5 ];
+
+ this._limits = new x3dom.arc.Limits(min, max);
+ this._factorGetterFunc = factorGetterFunc;
+ this._factorSetterFunc = factorSetterFunc;
+ this._setterFunc = setterFunc;
+ this._getterFunc = getterFunc;
+ this._dirFac = dirFac;
+
+ this.getFactor = function()
+ {
+ return this._factorGetterFunc();
+ };
+
+ this.update = function(state, step)
+ {
+ var stateVal = this._stateValue[state] + step * this._dirFac;
+ this._stateValue[state] = 0 <= stateVal ? ( 1 >= stateVal ? stateVal : 1 ) : 0;
+ this._setterFunc(this._limits.getValue(this._stateValue[state]));
+
+ //console.log(this.name +" "+this._factorGetterFunc() +" * " + step +" "+ this._stateValue[state] +" "+ state);
+ };
+
+ this.reset = function()
+ {
+ this._stateValue[0] = 0.5;
+ this._stateValue[1] = 0.5;
+ };
+};
+
+//---------------------------------------------------------------------------------------------------------------------
+
+x3dom.arc.AdaptiveRenderControl = defineClass(
+ null,
+ function(scene)
+ {
+ x3dom.arc.instance = this;
+
+ this._scene = scene;
+ this._targetFrameRate = [];
+ this._targetFrameRate[0] = this._scene._vf.minFrameRate;
+ this._targetFrameRate[1] = this._scene._vf.maxFrameRate;
+
+ this._currentState = 0;
+
+ var that = this;
+ var environment = that._scene.getEnvironment();
+
+ this._arfs = [];
+
+ this._arfs.push(
+ new x3dom.arc.ARF("smallFeatureCulling",
+ 0, 10, -1,
+ function()
+ {
+ return environment._vf.smallFeatureFactor;
+ },
+ function(value)
+ {
+ environment._vf.smallFeatureFactor = value;
+ },
+ function()
+ {
+ return environment._vf.smallFeatureThreshold;
+ },
+ function(value)
+ {
+ environment._vf.smallFeatureThreshold = value;
+ }
+ )
+ );
+
+ this._arfs.push(
+ new x3dom.arc.ARF("lowPriorityCulling",
+ 0,100,1,
+ function()
+ {
+ return environment._vf.lowPriorityFactor;
+ },
+ function(value)
+ {
+ environment._vf.lowPriorityFactor = value;
+ },
+ function()
+ {
+ return environment._vf.lowPriorityThreshold * 100;
+ },
+ function(value)
+ {
+ environment._vf.lowPriorityThreshold = value / 100;
+ }
+ )
+ );
+
+ this._arfs.push(
+ new x3dom.arc.ARF("tessellationDetailCulling",
+ 1,12,-1,
+ function()
+ {
+ return environment._vf.tessellationErrorFactor;
+ },
+ function(value)
+ {
+ environment._vf.tessellationErrorFactor = value;
+ },
+ //@todo: this factor is a static member of PopGeo... should it belong to scene instead?
+ function()
+ {
+ return environment.tessellationErrorThreshold;
+ },
+ function(value)
+ {
+ environment.tessellationErrorThreshold = value;
+ }
+ )
+ );
+
+ this._stepWidth = 0.1;
+ },
+ {
+ update : function(state, fps) // state: 0 = static, 1 : moving
+ {
+ this._currentState = state;
+ var delta = fps - this._targetFrameRate[state];
+
+ //to prevent flickering
+ this._stepWidth = Math.abs(delta) > 10 ? 0.1 : 0.01;
+
+ /*if( (delta > 0 && state == 1) || (delta < 0 && state == 0))
+ return;
+ */
+
+ var factorSum = 0;
+ var normFactors = [];
+
+ //normalize factors
+ var i, n = this._arfs.length;
+
+ for(i = 0; i < n; ++i)
+ {
+ normFactors[i] = this._arfs[i].getFactor();
+ if(normFactors[i] > 0)
+ factorSum += normFactors[i];
+ }
+
+ var dirFac = delta < 0 ? -1 : 1;
+ for(i = 0; i < n; ++i)
+ {
+ if(normFactors[i] > 0)
+ {
+ normFactors[i] /= factorSum;
+ this._arfs[i].update(state, this._stepWidth * normFactors[i] * dirFac);
+ }
+ }
+ },
+
+ reset: function()
+ {
+ for( var i = 0, n = this._arfs.length; i < n; ++i)
+ {
+ this._arfs[i].reset();
+ }
+ }
+ }
+);
+
+//---------------------------------------------------------------------------------------------------------------------
+
+
+/*
+ * X3DOM JavaScript Library
+ * http://www.x3dom.org
+ *
+ * (C)2009 Fraunhofer IGD, Darmstadt, Germany
+ * Dual licensed under the MIT and GPL
+ *
+ * Based on code originally provided by
+ * Philip Taylor: http://philip.html5.org
+ */
+
+
+ /**
+ * Class: x3dom.DownloadManager
+ *
+ * Simple priority-based download manager.
+ * Before objects of priority n+1 are available,
+ * all objects of priority n must have been already delivered.
+ * The highest priority key is 0.
+ *
+ */
+
+/// a small Request class
+var Request = function(url, onloadCallback, priority){
+ this.url = url;
+ this.priority = priority;
+ this.xhr = new XMLHttpRequest();
+ this.onloadCallbacks = [onloadCallback];
+
+ var self = this;
+
+ this.xhr.onload = function() {
+ if (x3dom.DownloadManager.debugOutput) {
+ x3dom.debug.logInfo('Download manager received data for URL \'' + self.url + '\'.');
+ }
+
+ --x3dom.DownloadManager.activeDownloads;
+
+ if ((x3dom.DownloadManager.stallToKeepOrder === false ) || (x3dom.DownloadManager.resultGetsStalled(self.priority) === false)) {
+ var i;
+ for (i = 0; i < self.onloadCallbacks.length; ++i) {
+ self.onloadCallbacks[i](self.xhr.response);
+ }
+
+ x3dom.DownloadManager.removeDownload(self);
+
+ x3dom.DownloadManager.updateStalledResults();
+ }
+ else if (x3dom.DownloadManager.debugOutput) {
+ x3dom.debug.logInfo('Download manager stalled downloaded result for URL \'' + self.url + '\'.');
+ }
+
+ x3dom.DownloadManager.tryNextDownload();
+ };
+};
+
+
+Request.prototype.send = function() {
+ this.xhr.open('GET', encodeURI(this.url), true); //asynchronous
+
+ //at the moment, ArrayBuffer is the only possible return type
+ this.xhr.responseType = 'arraybuffer';
+
+ this.xhr.send(null);
+
+ if (x3dom.DownloadManager.debugOutput) {
+ x3dom.debug.logInfo('Download manager posted XHR for URL \'' + this.url + '\'.');
+ }
+};
+
+
+x3dom.DownloadManager = {
+
+requests : [], //map priority->[requests]
+
+maxDownloads : 6, //number of max. concurrent downloads
+
+activeDownloads : 0, //number of active downloads
+
+debugOutput : false,
+
+stallToKeepOrder : false,
+
+
+toggleDebugOutput : function(flag) {
+ this.debugOutput = flag;
+},
+
+
+toggleStrictReturnOrder : function(flag) {
+ //@todo: this is not working properly yet!
+ this.stallToKeepOrder = false;
+ //this.stallToKeepOrder = flag;
+},
+
+
+removeDownload : function(req) {
+ var i, j;
+ var done = false;
+
+ for (i = 0; i < this.requests.length && !done; ++i) {
+ if (this.requests[i]){
+ for (j = 0; j < this.requests[i].length; ++j) {
+ if (this.requests[i][j] === req) {
+ this.requests[i].splice(j, 1);
+ done = true;
+ break;
+ }
+ }
+ }
+ }
+},
+
+
+tryNextDownload : function() {
+ var firstRequest;
+ var i, j;
+
+ //if there are less then maxDownloads running, start a new one,
+ //otherwise do nothing
+ if (this.activeDownloads < this.maxDownloads) {
+ //remove first queue element, if any
+ for (i = 0; i < this.requests.length && !firstRequest; ++i) {
+ //find the request queue with the highest priority
+ if (this.requests[i]) {
+ //remove first unsent request from the queue, if any
+ for (j = 0; j < this.requests[i].length; ++j) {
+ if (this.requests[i][j].xhr.readyState === XMLHttpRequest.UNSENT) {
+ firstRequest = this.requests[i][j];
+ break;
+ }
+ }
+ }
+ }
+
+ if (firstRequest) {
+ firstRequest.send();
+
+ ++this.activeDownloads;
+ }
+ }
+},
+
+
+resultGetsStalled : function(priority) {
+ var i;
+
+ for (i = 0; i < priority; ++i) {
+ if (this.requests[i] && this.requests[i].length) {
+ return true;
+ }
+ }
+
+ return false;
+},
+
+
+updateStalledResults : function() {
+ if (x3dom.DownloadManager.stallToKeepOrder) {
+ var i, j, k;
+ var req, pendingRequestFound = false;
+
+ for (i = 0; i < this.requests.length && !pendingRequestFound; ++i) {
+
+ if (this.requests[i]) {
+ for (j = 0; j < this.requests[i].length; ++j) {
+ //check if there is a stalled result and relase it, if so
+ req = this.requests[i][j];
+
+ if (req.xhr.readyState === XMLHttpRequest.DONE) {
+
+ if (x3dom.DownloadManager.debugOutput) {
+ x3dom.debug.logInfo('Download manager releases stalled result for URL \'' + req.url + '\'.');
+ }
+
+ for (k = 0; k < req.onloadCallbacks.length; ++k) {
+ req.onloadCallbacks[k](req.xhr.response);
+ }
+
+ //remove request from the list
+ this.requests[i].splice(j, 1);
+ }
+ //if there is an unfinished result, stop releasing results of lower priorities
+ else {
+ pendingRequestFound = true;
+ }
+ }
+ }
+
+ }
+ }
+},
+
+
+/**
+ * Requests a download from the given URL, with the given onloadCallback and priority.
+ * The callback function will be invoked with a JSON object as parameter, where the
+ * 'arrayBuffer' member contains a reference to the requested data and the 'url' member
+ * contains the original user-given URL of the object.
+ *
+ * If there is no data from the given url available, but there is already a registered request
+ * for it, the new callback is just appended to the old registered request object. Note that,
+ * in this special case, the priority of the old request is not changed, i.e. the priority
+ * of the new request to the same url is ignored.
+ */
+get : function(urls, onloadCallbacks, priorities) {
+ var i, j, k, r;
+ var found = false;
+ var url, onloadCallback, priority;
+
+ if (urls.length !== onloadCallbacks.length || urls.length !== priorities.length)
+ {
+ x3dom.debug.logError('DownloadManager: The number of given urls, onload callbacks and priorities is not equal. Ignoring requests.');
+ return;
+ }
+
+ //insert requests
+ for (k = 0; k < urls.length; ++k) {
+ if (!onloadCallbacks[k] === undefined || !priorities[k] === undefined) {
+ x3dom.debug.logError('DownloadManager: No onload callback and / or priority specified. Ignoring request for \"' + url + '\"');
+ continue;
+ }
+ else {
+ url = urls[k];
+ onloadCallback = onloadCallbacks[k];
+ priority = priorities[k];
+
+ //enqueue request priority-based or append callback to a matching active request
+
+ //check if there is already an enqueued or sent request for the given url
+ for (i = 0; i < this.requests.length && !found; ++i) {
+ if (this.requests[i]) {
+ for (j = 0; j < this.requests[i].length; ++j) {
+ if (this.requests[i][j].url === url) {
+ this.requests[i][j].onloadCallbacks.push(onloadCallback);
+
+ if (x3dom.DownloadManager.debugOutput) {
+ x3dom.debug.logInfo('Download manager appended onload callback for URL \'' + url + '\' to a registered request using the same URL.');
+ }
+
+ found = true;
+ break;
+ }
+ }
+ }
+ }
+
+ if (!found) {
+ r = new Request(url, onloadCallback, priority);
+
+ if (this.requests[priority]) {
+ this.requests[priority].push(r);
+ }
+ else {
+ this.requests[priority] = [r];
+ }
+ }
+ }
+ }
+
+ //try to download data
+ for (i = 0; i < urls.length && this.activeDownloads < this.maxDownloads; ++i) {
+ this.tryNextDownload();
+ }
+}
+
+};
+
+/**
+ * Created by tsturm on 30.10.2014.
+ */
+/**
+ * Parts Object is return
+ */
+x3dom.MultiMaterial = function( params )
+{
+ this._origAmbientIntensity = params.ambientIntensity;
+ this._origDiffuseColor = params.diffuseColor;
+ this._origEmissiveColor = params.emissiveColor;
+ this._origShininess = params.shininess;
+ this._origSpeclarColor = params.specularColor;
+ this._origTransparency = params.transparency;
+
+ this._origBackAmbientIntensity = params.backAmbientIntensity;
+ this._origBackDiffuseColor = params.backDiffuseColor;
+ this._origBackEmissiveColor = params.backEmissiveColor;
+ this._origBackShininess = params.backShininess;
+ this._origBackSpecularColor = params.backSpecularColor;
+ this._origBackTransparency = params.backTransparency;
+
+ this._ambientIntensity = params.ambientIntensity;
+ this._diffuseColor = params.diffuseColor;
+ this._emissiveColor = params.emissiveColor;
+ this._shininess = params.shininess;
+ this._specularColor = params.specularColor;
+ this._transparency = params.transparency;
+
+ this._backAmbientIntensity = params.backAmbientIntensity;
+ this._backDiffuseColor = params.backDiffuseColor;
+ this._backEmissiveColor = params.backEmissiveColor;
+ this._backShininess = params.backShininess;
+ this._backSpecularColor = params.backSpecularColor;
+ this._backTransparency = params.backTransparency;
+
+ this._highlighted = false;
+
+ this.reset = function () {
+ this._ambientIntensity = this._origAmbientIntensity;
+ this._diffuseColor = this._origDiffuseColor;
+ this._emissiveColor = this._origEmissiveColor;
+ this._shininess = this._origShininess;
+ this._specularColor = this._origSpeclarColor;
+ this._transparency = this._origTransparency;
+ this._backAmbientIntensity = this._origBackAmbientIntensity;
+ this._backDiffuseColor = this._origBackDiffuseColor;
+ this._backEmissiveColor = this._origBackEmissiveColor;
+ this._backShininess = this._origBackShininess;
+ this._backSpecularColor = this._origBackSpecularColor;
+ this._backTransparency = this._origBackTransparency;
+ };
+
+};
+/*
+ * X3DOM JavaScript Library
+ * http://www.x3dom.org
+ *
+ * (C)2009 Fraunhofer IGD, Darmstadt, Germany
+ * Dual licensed under the MIT and GPL
+ *
+ * Based on code originally provided by
+ * Philip Taylor: http://philip.html5.org
+ */
+
+
+/**
+ * Parts Object is return
+ */
+x3dom.Parts = function(multiPart, ids, colorMap, emissiveMap, specularMap, visibilityMap)
+{
+ var parts = this;
+ this.multiPart = multiPart;
+ this.ids = ids;
+ this.colorMap = colorMap;
+ this.emissiveMap = emissiveMap;
+ this.specularMap = specularMap;
+ this.visibilityMap = visibilityMap;
+ this.width = parts.colorMap.getWidth();
+ this.widthTwo = this.width * this.width;
+
+ /**
+ *
+ * @param color
+ * @param frontSide
+ */
+ this.setDiffuseColor = function(color, side)
+ {
+ var i, partID, pixelIDFront, pixelIDBack;
+
+ if(side == undefined && side != "front" && side != "back" && side != "both") {
+ side = "both";
+ }
+
+ color = x3dom.fields.SFColor.parse( color );
+
+ if (ids.length && ids.length > 1) //Multi select
+ {
+ //Get original pixels
+ var pixels = parts.colorMap.getPixels();
+
+ for ( i=0; i < parts.ids.length; i++ )
+ {
+ partID = parts.ids[i];
+ pixelIDFront = partID;
+ pixelIDBack = partID + this.widthTwo;
+
+ //Check for front/back
+ if (side == "front")
+ {
+ this.multiPart._materials[partID]._diffuseColor = color;
+ }
+ else if(side == "back")
+ {
+ this.multiPart._materials[partID]._backDiffuseColor = color;
+ }
+ else if(side == "both")
+ {
+ this.multiPart._materials[partID]._diffuseColor = color;
+ this.multiPart._materials[partID]._backDiffuseColor = color;
+ }
+
+ //If part is not highlighted update the pixel
+ if ( !this.multiPart._materials[partID]._highlighted )
+ {
+ if (side == "front") {
+ pixels[pixelIDFront].r = color.r;
+ pixels[pixelIDFront].g = color.g;
+ pixels[pixelIDFront].b = color.b;
+ } else if(side == "back") {
+ pixels[pixelIDBack].r = color.r;
+ pixels[pixelIDBack].g = color.g;
+ pixels[pixelIDBack].b = color.b;
+ } else if(side == "both") {
+ pixels[pixelIDFront].r = color.r;
+ pixels[pixelIDFront].g = color.g;
+ pixels[pixelIDFront].b = color.b;
+ pixels[pixelIDBack].r = color.r;
+ pixels[pixelIDBack].g = color.g;
+ pixels[pixelIDBack].b = color.b;
+ }
+ }
+ }
+
+ parts.colorMap.setPixels(pixels);
+ }
+ else
+ {
+ var xFront, yFront, xBack, yBack, pixelFront, pixelBack;
+
+ partID = parts.ids[0];
+ pixelIDFront = partID;
+ pixelIDBack = partID + this.widthTwo;
+
+ //Check for front/back
+ if (side == "front")
+ {
+ xFront = pixelIDFront % this.width;
+ yFront = Math.floor(pixelIDFront / this.width);
+ pixelFront = parts.colorMap.getPixel(xFront, yFront);
+ this.multiPart._materials[partID]._diffuseColor = color;
+ }
+ else if(side == "back")
+ {
+ xBack = pixelIDBack % this.width;
+ yBack = Math.floor(pixelIDBack / this.width);
+ pixelBack = parts.colorMap.getPixel(xBack, yBack);
+ this.multiPart._materials[partID]._backDiffuseColor = color;
+ }
+ else if(side == "both")
+ {
+ xFront = pixelIDFront % this.width;
+ yFront = Math.floor(pixelIDFront / this.width);
+ xBack = pixelIDBack % this.width;
+ yBack = Math.floor(pixelIDBack / this.width);
+ pixelFront = parts.colorMap.getPixel(xFront, yFront);
+ pixelBack = parts.colorMap.getPixel(xBack, yBack);
+ this.multiPart._materials[partID]._diffuseColor = color;
+ this.multiPart._materials[partID]._backDiffuseColor = color;
+ }
+
+ //If part is not highlighted update the pixel
+ if ( !this.multiPart._materials[partID]._highlighted )
+ {
+ if (side == "front")
+ {
+ pixelFront.r = color.r;
+ pixelFront.g = color.g;
+ pixelFront.b = color.b;
+
+ parts.colorMap.setPixel(x, y, pixelFront);
+ }
+ else if(side == "back")
+ {
+ pixelBack.r = color.r;
+ pixelBack.g = color.g;
+ pixelBack.b = color.b;
+
+ parts.colorMap.setPixel(x, y, pixelBack);
+ }
+ else if(side == "both")
+ {
+ pixelFront.r = color.r;
+ pixelFront.g = color.g;
+ pixelFront.b = color.b;
+ pixelBack.r = color.r;
+ pixelBack.g = color.g;
+ pixelBack.b = color.b;
+
+ parts.colorMap.setPixel(x, y, pixelFront);
+ parts.colorMap.setPixel(x, y, pixelBack);
+ }
+ }
+ }
+ };
+
+ /**
+ *
+ * @param frontSide
+ * @returns {*}
+ */
+ this.getDiffuseColor = function(side)
+ {
+ var i, partID;
+
+ if(side == undefined && side != "front" && side != "back") {
+ side = "front";
+ }
+
+ if (ids.length && ids.length > 1) //Multi select
+ {
+ var diffuseColors = [];
+
+ for ( i=0; i < parts.ids.length; i++ )
+ {
+ partID = parts.ids[i];
+
+ if(side == "front")
+ {
+ diffuseColors.push(this.multiPart._materials[partID]._diffuseColor);
+ }
+ else if(side == "back")
+ {
+ diffuseColors.push(this.multiPart._materials[partID]._backDiffuseColor);
+ }
+ }
+ return diffuseColors;
+ }
+ else
+ {
+ partID = parts.ids[0];
+
+ if(side == "front")
+ {
+ return this.multiPart._materials[partID]._diffuseColor;
+ }
+ else if(side == "back")
+ {
+ return this.multiPart._materials[partID]._backDiffuseColor;
+ }
+ }
+ };
+
+ /**
+ *
+ * @param color
+ * @param side
+ */
+ this.setEmissiveColor = function(color, side)
+ {
+ var i, partID, pixelIDFront, pixelIDBack;
+
+ if(side == undefined && side != "front" && side != "back" && side != "both") {
+ side = "both";
+ }
+
+ color = x3dom.fields.SFColor.parse( color );
+
+ if (ids.length && ids.length > 1) //Multi select
+ {
+ //Get original pixels
+ var pixels = parts.emissiveMap.getPixels();
+
+ for ( i=0; i < parts.ids.length; i++ )
+ {
+ partID = parts.ids[i];
+ pixelIDFront = partID;
+ pixelIDBack = partID + this.widthTwo;
+
+ //Check for front/back
+ if (side == "front")
+ {
+ this.multiPart._materials[partID]._emissiveColor = color;
+ }
+ else if(side == "back")
+ {
+ this.multiPart._materials[partID]._backEmissiveColor = color;
+ }
+ else if(side == "both")
+ {
+ this.multiPart._materials[partID]._emissiveColor = color;
+ this.multiPart._materials[partID]._backEmissiveColor = color;
+ }
+
+ //If part is not highlighted update the pixel
+ if ( !this.multiPart._materials[partID]._highlighted )
+ {
+ if (side == "front") {
+ pixels[pixelIDFront].r = color.r;
+ pixels[pixelIDFront].g = color.g;
+ pixels[pixelIDFront].b = color.b;
+ } else if(side == "back") {
+ pixels[pixelIDBack].r = color.r;
+ pixels[pixelIDBack].g = color.g;
+ pixels[pixelIDBack].b = color.b;
+ } else if(side == "both") {
+ pixels[pixelIDFront].r = color.r;
+ pixels[pixelIDFront].g = color.g;
+ pixels[pixelIDFront].b = color.b;
+ pixels[pixelIDBack].r = color.r;
+ pixels[pixelIDBack].g = color.g;
+ pixels[pixelIDBack].b = color.b;
+ }
+ }
+ }
+
+ parts.emissiveMap.setPixels(pixels);
+ }
+ else
+ {
+ var xFront, yFront, xBack, yBack, pixelFront, pixelBack;
+
+ partID = parts.ids[0];
+ pixelIDFront = partID;
+ pixelIDBack = partID + this.widthTwo;
+
+ //Check for front/back
+ if (side == "front")
+ {
+ xFront = pixelIDFront % this.width;
+ yFront = Math.floor(pixelIDFront / this.width);
+ pixelFront = parts.emissiveMap.getPixel(xFront, yFront);
+ this.multiPart._materials[partID]._emissiveColor = color;
+ }
+ else if(side == "back")
+ {
+ xBack = pixelIDBack % this.width;
+ yBack = Math.floor(pixelIDBack / this.width);
+ pixelBack = parts.emissiveMap.getPixel(xBack, yBack);
+ this.multiPart._materials[partID]._backEmissiveColor = color;
+ }
+ else if(side == "both")
+ {
+ xFront = pixelIDFront % this.width;
+ yFront = Math.floor(pixelIDFront / this.width);
+ xBack = pixelIDBack % this.width;
+ yBack = Math.floor(pixelIDBack / this.width);
+ pixelFront = parts.emissiveMap.getPixel(xFront, yFront);
+ pixelBack = parts.emissiveMap.getPixel(xBack, yBack);
+ this.multiPart._materials[partID]._emissiveColor = color;
+ this.multiPart._materials[partID]._backEmissiveColor = color;
+ }
+
+ //If part is not highlighted update the pixel
+ if ( !this.multiPart._materials[partID]._highlighted )
+ {
+ if (side == "front")
+ {
+ pixelFront.r = color.r;
+ pixelFront.g = color.g;
+ pixelFront.b = color.b;
+
+ parts.emissiveMap.setPixel(x, y, pixelFront);
+ }
+ else if(side == "back")
+ {
+ pixelBack.r = color.r;
+ pixelBack.g = color.g;
+ pixelBack.b = color.b;
+
+ parts.emissiveMap.setPixel(x, y, pixelBack);
+ }
+ else if(side == "both")
+ {
+ pixelFront.r = color.r;
+ pixelFront.g = color.g;
+ pixelFront.b = color.b;
+ pixelBack.r = color.r;
+ pixelBack.g = color.g;
+ pixelBack.b = color.b;
+
+ parts.emissiveMap.setPixel(x, y, pixelFront);
+ parts.emissiveMap.setPixel(x, y, pixelBack);
+ }
+ }
+ }
+ };
+
+ /**
+ *
+ * @param side
+ * @returns {*}
+ */
+ this.getEmissiveColor = function(side)
+ {
+ var i, partID;
+
+ if(side == undefined && side != "front" && side != "back") {
+ side = "front";
+ }
+
+ if (ids.length && ids.length > 1) //Multi select
+ {
+ var emissiveColors = [];
+
+ for ( i=0; i < parts.ids.length; i++ )
+ {
+ partID = parts.ids[i];
+
+ if(side == "front")
+ {
+ emissiveColors.push(this.multiPart._materials[partID]._emissiveColor);
+ }
+ else if(side == "back")
+ {
+ emissiveColors.push(this.multiPart._materials[partID]._backEmissiveColor);
+ }
+ }
+ return emissiveColors;
+ }
+ else
+ {
+ partID = parts.ids[0];
+
+ if(side == "front")
+ {
+ return this.multiPart._materials[partID]._emissiveColor;
+ }
+ else if(side == "back")
+ {
+ return this.multiPart._materials[partID]._backEmissiveColor;
+ }
+ }
+ };
+
+ /**
+ *
+ * @param color
+ * @param side
+ */
+ this.setSpecularColor = function(color, side)
+ {
+ var i, partID, pixelIDFront, pixelIDBack;
+
+ if(side == undefined && side != "front" && side != "back" && side != "both") {
+ side = "both";
+ }
+
+ color = x3dom.fields.SFColor.parse( color );
+
+ if (ids.length && ids.length > 1) //Multi select
+ {
+ //Get original pixels
+ var pixels = parts.specularMap.getPixels();
+
+ for ( i=0; i < parts.ids.length; i++ )
+ {
+ partID = parts.ids[i];
+ pixelIDFront = partID;
+ pixelIDBack = partID + this.widthTwo;
+
+ //Check for front/back
+ if (side == "front")
+ {
+ this.multiPart._materials[partID]._specularColor = color;
+ }
+ else if(side == "back")
+ {
+ this.multiPart._materials[partID]._backSpecularColor = color;
+ }
+ else if(side == "both")
+ {
+ this.multiPart._materials[partID]._specularColor = color;
+ this.multiPart._materials[partID]._backSpecularColor = color;
+ }
+
+ //If part is not highlighted update the pixel
+ if ( !this.multiPart._materials[partID]._highlighted )
+ {
+ if (side == "front") {
+ pixels[pixelIDFront].r = color.r;
+ pixels[pixelIDFront].g = color.g;
+ pixels[pixelIDFront].b = color.b;
+ } else if(side == "back") {
+ pixels[pixelIDBack].r = color.r;
+ pixels[pixelIDBack].g = color.g;
+ pixels[pixelIDBack].b = color.b;
+ } else if(side == "both") {
+ pixels[pixelIDFront].r = color.r;
+ pixels[pixelIDFront].g = color.g;
+ pixels[pixelIDFront].b = color.b;
+ pixels[pixelIDBack].r = color.r;
+ pixels[pixelIDBack].g = color.g;
+ pixels[pixelIDBack].b = color.b;
+ }
+ }
+ }
+
+ parts.specularMap.setPixels(pixels);
+ }
+ else
+ {
+ var xFront, yFront, xBack, yBack, pixelFront, pixelBack;
+
+ partID = parts.ids[0];
+ pixelIDFront = partID;
+ pixelIDBack = partID + this.widthTwo;
+
+ //Check for front/back
+ if (side == "front")
+ {
+ xFront = pixelIDFront % this.width;
+ yFront = Math.floor(pixelIDFront / this.width);
+ pixelFront = parts.specularMap.getPixel(xFront, yFront);
+ this.multiPart._materials[partID]._specularColor = color;
+ }
+ else if(side == "back")
+ {
+ xBack = pixelIDBack % this.width;
+ yBack = Math.floor(pixelIDBack / this.width);
+ pixelBack = parts.specularMap.getPixel(xBack, yBack);
+ this.multiPart._materials[partID]._backSpecularColor = color;
+ }
+ else if(side == "both")
+ {
+ xFront = pixelIDFront % this.width;
+ yFront = Math.floor(pixelIDFront / this.width);
+ xBack = pixelIDBack % this.width;
+ yBack = Math.floor(pixelIDBack / this.width);
+ pixelFront = parts.specularMap.getPixel(xFront, yFront);
+ pixelBack = parts.specularMap.getPixel(xBack, yBack);
+ this.multiPart._materials[partID]._specularColor = color;
+ this.multiPart._materials[partID]._backSpecularColor = color;
+ }
+
+ //If part is not highlighted update the pixel
+ if ( !this.multiPart._materials[partID]._highlighted )
+ {
+ if (side == "front")
+ {
+ pixelFront.r = color.r;
+ pixelFront.g = color.g;
+ pixelFront.b = color.b;
+
+ parts.specularMap.setPixel(x, y, pixelFront);
+ }
+ else if(side == "back")
+ {
+ pixelBack.r = color.r;
+ pixelBack.g = color.g;
+ pixelBack.b = color.b;
+
+ parts.specularMap.setPixel(x, y, pixelBack);
+ }
+ else if(side == "both")
+ {
+ pixelFront.r = color.r;
+ pixelFront.g = color.g;
+ pixelFront.b = color.b;
+ pixelBack.r = color.r;
+ pixelBack.g = color.g;
+ pixelBack.b = color.b;
+
+ parts.specularMap.setPixel(x, y, pixelFront);
+ parts.specularMap.setPixel(x, y, pixelBack);
+ }
+ }
+ }
+ };
+
+
+ /**
+ *
+ * @param side
+ * @returns {*}
+ */
+ this.getSpecularColor = function(side)
+ {
+ var i, partID;
+
+ if(side == undefined && side != "front" && side != "back") {
+ side = "front";
+ }
+
+ if (ids.length && ids.length > 1) //Multi select
+ {
+ var specularColors = [];
+
+ for ( i=0; i < parts.ids.length; i++ )
+ {
+ partID = parts.ids[i];
+
+ if(side == "front")
+ {
+ specularColors.push(this.multiPart._materials[partID]._specularColor);
+ }
+ else if(side == "back")
+ {
+ specularColors.push(this.multiPart._materials[partID]._backSpecularColor);
+ }
+ }
+ return specularColors;
+ }
+ else
+ {
+ partID = parts.ids[0];
+
+ if(side == "front")
+ {
+ return this.multiPart._materials[partID]._specularColor;
+ }
+ else if(side == "back")
+ {
+ return this.multiPart._materials[partID]._backSpecularColor;
+ }
+ }
+ };
+
+ /**
+ *
+ * @param transparency
+ * @param side
+ */
+ this.setTransparency = function(transparency, side)
+ {
+ var i, partID, pixelIDFront, pixelIDBack;
+
+ if(side == undefined && side != "front" && side != "back" && side != "both") {
+ side = "both";
+ }
+
+ if (ids.length && ids.length > 1) //Multi select
+ {
+ //Get original pixels
+ var pixels = parts.colorMap.getPixels();
+
+ for ( i=0; i < parts.ids.length; i++ )
+ {
+ partID = parts.ids[i];
+ pixelIDFront = partID;
+ pixelIDBack = partID + this.widthTwo;
+
+ //Check for front/back
+ if (side == "front")
+ {
+ this.multiPart._materials[partID]._transparency = transparency;
+ }
+ else if(side == "back")
+ {
+ this.multiPart._materials[partID]._backTransparency = transparency;
+ }
+ else if(side == "both")
+ {
+ this.multiPart._materials[partID]._transparency = transparency;
+ this.multiPart._materials[partID]._backTransparency = transparency;
+ }
+
+ //If part is not highlighted update the pixel
+ if ( !this.multiPart._materials[partID]._highlighted )
+ {
+ if (side == "front") {
+ pixels[pixelIDFront].a = 1.0 - transparency;
+ } else if(side == "back") {
+ pixels[pixelIDBack].a = 1.0 - transparency;
+ } else if(side == "both") {
+ pixels[pixelIDFront].a = 1.0 - transparency;
+ pixels[pixelIDBack].a = 1.0 - transparency;
+ }
+ }
+ }
+
+ parts.colorMap.setPixels(pixels);
+ }
+ else
+ {
+ var xFront, yFront, xBack, yBack, pixelFront, pixelBack;
+
+ partID = parts.ids[0];
+ pixelIDFront = partID;
+ pixelIDBack = partID + this.widthTwo;
+
+ //Check for front/back
+ if (side == "front")
+ {
+ xFront = pixelIDFront % this.width;
+ yFront = Math.floor(pixelIDFront / this.width);
+ pixelFront = parts.colorMap.getPixel(xFront, yFront);
+ this.multiPart._materials[partID]._transparency = transparency;
+ }
+ else if(side == "back")
+ {
+ xBack = pixelIDBack % this.width;
+ yBack = Math.floor(pixelIDBack / this.width);
+ pixelBack = parts.colorMap.getPixel(xBack, yBack);
+ this.multiPart._materials[partID]._backTransparency = transparency;
+ }
+ else if(side == "both")
+ {
+ xFront = pixelIDFront % this.width;
+ yFront = Math.floor(pixelIDFront / this.width);
+ xBack = pixelIDBack % this.width;
+ yBack = Math.floor(pixelIDBack / this.width);
+ pixelFront = parts.colorMap.getPixel(xFront, yFront);
+ pixelBack = parts.colorMap.getPixel(xBack, yBack);
+ this.multiPart._materials[partID]._transparency = transparency;
+ this.multiPart._materials[partID]._backTransparency = transparency;
+ }
+
+ //If part is not highlighted update the pixel
+ if ( !this.multiPart._materials[partID]._highlighted )
+ {
+ if (side == "front")
+ {
+ pixelFront.a = 1.0 - transparency;
+
+ parts.colorMap.setPixel(x, y, pixelFront);
+ }
+ else if(side == "back")
+ {
+ pixelBack.a = 1.0 - transparency;
+
+ parts.colorMap.setPixel(x, y, pixelBack);
+ }
+ else if(side == "both")
+ {
+ pixelFront.a = 1.0 - transparency;
+ pixelBack.a = 1.0 - transparency;
+
+ parts.colorMap.setPixel(x, y, pixelFront);
+ parts.colorMap.setPixel(x, y, pixelBack);
+ }
+ }
+ }
+ };
+
+
+ /**
+ *
+ * @param side
+ * @returns {*}
+ */
+ this.getTransparency = function(side)
+ {
+ var i, partID;
+
+ if(side == undefined && side != "front" && side != "back") {
+ side = "front";
+ }
+
+ if (ids.length && ids.length > 1) //Multi select
+ {
+ var transparencies = [];
+
+ for ( i=0; i < parts.ids.length; i++ )
+ {
+ partID = parts.ids[i];
+
+ if(side == "front")
+ {
+ transparencies.push(this.multiPart._materials[partID]._transparency);
+ }
+ else if(side == "back")
+ {
+ transparencies.push(this.multiPart._materials[partID]._backTransparency);
+ }
+ }
+ return transparencies;
+ }
+ else
+ {
+ partID = parts.ids[0];
+
+ if(side == "front")
+ {
+ return this.multiPart._materials[partID]._transparency;
+ }
+ else if(side == "back")
+ {
+ return this.multiPart._materials[partID]._backTransparency;
+ }
+ }
+ };
+
+ /**
+ *
+ * @param shininess
+ * @param frontSide
+ */
+ this.setShininess = function(shininess, side)
+ {
+ var i, partID, pixelIDFront, pixelIDBack;
+
+ if(side == undefined && side != "front" && side != "back" && side != "both") {
+ side = "both";
+ }
+
+ if (ids.length && ids.length > 1) //Multi select
+ {
+ //Get original pixels
+ var pixels = parts.specularMap.getPixels();
+
+ for ( i=0; i < parts.ids.length; i++ )
+ {
+ partID = parts.ids[i];
+ pixelIDFront = partID;
+ pixelIDBack = partID + this.widthTwo;
+
+ //Check for front/back
+ if (side == "front")
+ {
+ this.multiPart._materials[partID]._shininess = shininess;
+ }
+ else if(side == "back")
+ {
+ this.multiPart._materials[partID]._backShininess = shininess;
+ }
+ else if(side == "both")
+ {
+ this.multiPart._materials[partID]._shininess = shininess;
+ this.multiPart._materials[partID]._backShininess = shininess;
+ }
+
+ //If part is not highlighted update the pixel
+ if ( !this.multiPart._materials[partID]._highlighted )
+ {
+ if (side == "front") {
+ pixels[pixelIDFront].a = shininess;
+ } else if(side == "back") {
+ pixels[pixelIDBack].a = shininess;
+ } else if(side == "both") {
+ pixels[pixelIDFront].a = shininess;
+ pixels[pixelIDBack].a = shininess;
+ }
+ }
+ }
+
+ parts.specularMap.setPixels(pixels);
+ }
+ else
+ {
+ var xFront, yFront, xBack, yBack, pixelFront, pixelBack;
+
+ partID = parts.ids[0];
+ pixelIDFront = partID;
+ pixelIDBack = partID + this.widthTwo;
+
+ //Check for front/back
+ if (side == "front")
+ {
+ xFront = pixelIDFront % this.width;
+ yFront = Math.floor(pixelIDFront / this.width);
+ pixelFront = parts.specularMap.getPixel(xFront, yFront);
+ this.multiPart._materials[partID]._shininess = shininess;
+ }
+ else if(side == "back")
+ {
+ xBack = pixelIDBack % this.width;
+ yBack = Math.floor(pixelIDBack / this.width);
+ pixelBack = parts.specularMap.getPixel(xBack, yBack);
+ this.multiPart._materials[partID]._backShininess = shininess;
+ }
+ else if(side == "both")
+ {
+ xFront = pixelIDFront % this.width;
+ yFront = Math.floor(pixelIDFront / this.width);
+ xBack = pixelIDBack % this.width;
+ yBack = Math.floor(pixelIDBack / this.width);
+ pixelFront = parts.specularMap.getPixel(xFront, yFront);
+ pixelBack = parts.specularMap.getPixel(xBack, yBack);
+ this.multiPart._materials[partID]._shininess = shininess;
+ this.multiPart._materials[partID]._backShininess = shininess;
+ }
+
+ //If part is not highlighted update the pixel
+ if ( !this.multiPart._materials[partID]._highlighted )
+ {
+ if (side == "front")
+ {
+ pixelFront.a = shininess;
+
+ parts.specularMap.setPixel(x, y, pixelFront);
+ }
+ else if(side == "back")
+ {
+ pixelBack.a = shininess;
+
+ parts.specularMap.setPixel(x, y, pixelBack);
+ }
+ else if(side == "both")
+ {
+ pixelFront.a = shininess;
+ pixelBack.a = shininess;
+
+ parts.specularMap.setPixel(x, y, pixelFront);
+ parts.specularMap.setPixel(x, y, pixelBack);
+ }
+ }
+ }
+ };
+
+
+ /**
+ *
+ * @param side
+ * @returns {*}
+ */
+ this.getShininess = function(side)
+ {
+ var i, partID;
+
+ if(side == undefined && side != "front" && side != "back") {
+ side = "front";
+ }
+
+ if (ids.length && ids.length > 1) //Multi select
+ {
+ var shininesses = [];
+
+ for ( i=0; i < parts.ids.length; i++ )
+ {
+ partID = parts.ids[i];
+
+ if(side == "front")
+ {
+ shininesses.push(this.multiPart._materials[partID]._shininess);
+ }
+ else if(side == "back")
+ {
+ shininesses.push(this.multiPart._materials[partID]._backShininess);
+ }
+ }
+ return shininesses;
+ }
+ else
+ {
+ partID = parts.ids[0];
+
+ if(side == "front")
+ {
+ return this.multiPart._materials[partID]._shininess;
+ }
+ else if(side == "back")
+ {
+ return this.multiPart._materials[partID]._backShininess;
+ }
+ }
+ };
+
+ /**
+ *
+ * @param ambientIntensity
+ * @param side
+ */
+ this.setAmbientIntensity = function(ambientIntensity, side)
+ {
+ var i, partID, pixelIDFront, pixelIDBack;
+
+ if(side == undefined && side != "front" && side != "back" && side != "both") {
+ side = "both";
+ }
+
+ if (ids.length && ids.length > 1) //Multi select
+ {
+ //Get original pixels
+ var pixels = parts.emissiveMap.getPixels();
+
+ for ( i=0; i < parts.ids.length; i++ )
+ {
+ partID = parts.ids[i];
+ pixelIDFront = partID;
+ pixelIDBack = partID + this.widthTwo;
+
+ //Check for front/back
+ if (side == "front")
+ {
+ this.multiPart._materials[partID]._ambientIntensity = ambientIntensity;
+ }
+ else if(side == "back")
+ {
+ this.multiPart._materials[partID]._backAmbientIntensity = ambientIntensity;
+ }
+ else if(side == "both")
+ {
+ this.multiPart._materials[partID]._ambientIntensity = ambientIntensity;
+ this.multiPart._materials[partID]._backAmbientIntensity = ambientIntensity;
+ }
+
+ //If part is not highlighted update the pixel
+ if ( !this.multiPart._materials[partID]._highlighted )
+ {
+ if (side == "front") {
+ pixels[pixelIDFront].a = ambientIntensity;
+ } else if(side == "back") {
+ pixels[pixelIDBack].a = ambientIntensity;
+ } else if(side == "both") {
+ pixels[pixelIDFront].a = ambientIntensity;
+ pixels[pixelIDBack].a = ambientIntensity;
+ }
+ }
+ }
+
+ parts.emissiveMap.setPixels(pixels);
+ }
+ else
+ {
+ var xFront, yFront, xBack, yBack, pixelFront, pixelBack;
+
+ partID = parts.ids[0];
+ pixelIDFront = partID;
+ pixelIDBack = partID + this.widthTwo;
+
+ //Check for front/back
+ if (side == "front")
+ {
+ xFront = pixelIDFront % this.width;
+ yFront = Math.floor(pixelIDFront / this.width);
+ pixelFront = parts.emissiveMap.getPixel(xFront, yFront);
+ this.multiPart._materials[partID]._ambientIntensity = ambientIntensity;
+ }
+ else if(side == "back")
+ {
+ xBack = pixelIDBack % this.width;
+ yBack = Math.floor(pixelIDBack / this.width);
+ pixelBack = parts.emissiveMap.getPixel(xBack, yBack);
+ this.multiPart._materials[partID]._backAmbientIntensity = ambientIntensity;
+ }
+ else if(side == "both")
+ {
+ xFront = pixelIDFront % this.width;
+ yFront = Math.floor(pixelIDFront / this.width);
+ xBack = pixelIDBack % this.width;
+ yBack = Math.floor(pixelIDBack / this.width);
+ pixelFront = parts.emissiveMap.getPixel(xFront, yFront);
+ pixelBack = parts.emissiveMap.getPixel(xBack, yBack);
+ this.multiPart._materials[partID]._ambientIntensity = ambientIntensity;
+ this.multiPart._materials[partID]._backAmbientIntensity = ambientIntensity;
+ }
+
+ //If part is not highlighted update the pixel
+ if ( !this.multiPart._materials[partID]._highlighted )
+ {
+ if (side == "front")
+ {
+ pixelFront.a = ambientIntensity;
+
+ parts.emissiveMap.setPixel(x, y, pixelFront);
+ }
+ else if(side == "back")
+ {
+ pixelBack.a = ambientIntensity;
+
+ parts.emissiveMap.setPixel(x, y, pixelBack);
+ }
+ else if(side == "both")
+ {
+ pixelFront.a = ambientIntensity;
+ pixelBack.a = ambientIntensity;
+
+ parts.emissiveMap.setPixel(x, y, pixelFront);
+ parts.emissiveMap.setPixel(x, y, pixelBack);
+ }
+ }
+ }
+ };
+
+
+ /**
+ *
+ * @param side
+ * @returns {*}
+ */
+ this.getAmbientIntensity = function(side)
+ {
+ var i, partID;
+
+ if(side == undefined && side != "front" && side != "back") {
+ side = "front";
+ }
+
+ if (ids.length && ids.length > 1) //Multi select
+ {
+ var ambientIntensities = [];
+
+ for ( i=0; i < parts.ids.length; i++ )
+ {
+ partID = parts.ids[i];
+
+ if(side == "front")
+ {
+ ambientIntensities.push(this.multiPart._materials[partID]._ambientIntensity);
+ }
+ else if(side == "back")
+ {
+ ambientIntensities.push(this.multiPart._materials[partID]._backAmbientIntensity);
+ }
+ }
+ return ambientIntensities;
+ }
+ else
+ {
+ partID = parts.ids[0];
+
+ if(side == "front")
+ {
+ return this.multiPart._materials[partID]._ambientIntensity;
+ }
+ else if(side == "back")
+ {
+ return this.multiPart._materials[partID]._backAmbientIntensity;
+ }
+ }
+ };
+
+ /**
+ *
+ * @param color
+ */
+ this.highlight = function (color)
+ {
+ var i, partID, pixelIDFront, pixelIDBack, dtColor, eaColor, ssColor;
+
+ color = x3dom.fields.SFColor.parse( color );
+
+ if (ids.length && ids.length > 1) //Multi select
+ {
+ //Get original pixels
+ var dtPixels = parts.colorMap.getPixels();
+ var eaPixels = parts.emissiveMap.getPixels();
+ var ssPixels = parts.specularMap.getPixels();
+
+ dtColor = new x3dom.fields.SFColorRGBA(0, 0, 0, 1.0);
+ eaColor = new x3dom.fields.SFColorRGBA(color.r, color.g, color.b, 0);
+ ssColor = new x3dom.fields.SFColorRGBA(0, 0, 0, 0);
+
+ for ( i=0; i < parts.ids.length; i++ ) {
+ partID = parts.ids[i];
+ pixelIDFront = partID;
+ pixelIDBack = partID + this.widthTwo;
+
+ if( !this.multiPart._materials[partID]._highlighted )
+ {
+ this.multiPart._materials[partID]._highlighted = true;
+
+ dtPixels[pixelIDFront] = dtColor;
+ eaPixels[pixelIDFront] = eaColor;
+ ssPixels[pixelIDFront] = ssColor;
+
+ dtPixels[pixelIDBack] = dtColor;
+ eaPixels[pixelIDBack] = eaColor;
+ ssPixels[pixelIDBack] = ssColor;
+ }
+ }
+
+ this.colorMap.setPixels(dtPixels, false);
+ this.emissiveMap.setPixels(eaPixels, false);
+ this.specularMap.setPixels(ssPixels, true);
+ }
+ else
+ {
+ partID = parts.ids[0];
+ pixelIDFront = partID;
+ pixelIDBack = partID + this.widthTwo;
+
+ var xFront = pixelIDFront % this.width;
+ var yFront = Math.floor(pixelIDFront / this.width);
+
+ var xBack = pixelIDBack % this.width;
+ var yBack = Math.floor(pixelIDBack / this.width);
+
+ //If part is not highlighted update the pixel
+ if ( !this.multiPart._materials[partID]._highlighted )
+ {
+ this.multiPart._materials[partID]._highlighted = true;
+
+ dtColor = new x3dom.fields.SFColorRGBA(0, 0, 0, 1);
+ eaColor = new x3dom.fields.SFColorRGBA(color.r, color.g, color.b, 0);
+ ssColor = new x3dom.fields.SFColorRGBA(0, 0, 0, 0);
+
+ this.colorMap.setPixel(xFront, yFront, dtColor, false);
+ this.emissiveMap.setPixel(xFront, yFront, eaColor, false);
+ this.specularMap.setPixel(xFront, yFront, ssColor, false);
+
+ this.colorMap.setPixel(xBack, yBack, dtColor, false);
+ this.emissiveMap.setPixel(xBack, yBack, eaColor, false);
+ this.specularMap.setPixel(xBack, yBack, ssColor, true);
+ }
+ }
+ };
+
+ this.unhighlight = function() {
+ var i, partID, pixelIDFront, pixelIDBack, material;
+ var dtColorFront, eaColorFront, ssColorFront;
+ var dtColorBack, eaColorBack, ssColorBack;
+
+ if (ids.length && ids.length > 1) //Multi select
+ {
+ //Get original pixels
+ var dtPixels = parts.colorMap.getPixels();
+ var eaPixels = parts.emissiveMap.getPixels();
+ var ssPixels = parts.specularMap.getPixels();
+
+ for ( i=0; i < parts.ids.length; i++ ) {
+ partID = parts.ids[i];
+ pixelIDFront = partID;
+ pixelIDBack = partID + this.widthTwo;
+
+ material = this.multiPart._materials[partID];
+
+ if( material._highlighted )
+ {
+ material._highlighted = false;
+
+ dtPixels[pixelIDFront] = new x3dom.fields.SFColorRGBA(material._diffuseColor.r, material._diffuseColor.g,
+ material._diffuseColor.b, 1.0 - material._transparency);
+ eaPixels[pixelIDFront] = new x3dom.fields.SFColorRGBA(material._emissiveColor.r, material._emissiveColor.g,
+ material._emissiveColor.b, material._ambientIntensity);
+ ssPixels[pixelIDFront] = new x3dom.fields.SFColorRGBA(material._specularColor.r, material._specularColor.g,
+ material._specularColor.b, material._shininess);
+
+ dtPixels[pixelIDBack] = new x3dom.fields.SFColorRGBA(material._backDiffuseColor.r, material._backDiffuseColor.g,
+ material._backDiffuseColor.b, 1.0 - material._backTransparency);
+ eaPixels[pixelIDBack] = new x3dom.fields.SFColorRGBA(material._backEmissiveColor.r, material._backEmissiveColor.g,
+ material._backEmissiveColor.b, material._backAmbientIntensity);
+ ssPixels[pixelIDBack] = new x3dom.fields.SFColorRGBA(material._backSpecularColor.r, material._backSpecularColor.g,
+ material._backSpecularColor.b, material._backShininess);
+ }
+ }
+
+ this.colorMap.setPixels(dtPixels, false);
+ this.emissiveMap.setPixels(eaPixels, false);
+ this.specularMap.setPixels(ssPixels, true);
+ }
+ else
+ {
+ partID = parts.ids[0];
+ pixelIDFront = partID;
+ pixelIDBack = partID + this.widthTwo;
+
+ var xFront = pixelIDFront % this.width;
+ var yFront = Math.floor(pixelIDFront / this.width);
+
+ var xBack = pixelIDBack % this.width;
+ var yBack = Math.floor(pixelIDBack / this.width);
+
+ material = this.multiPart._materials[partID];
+
+ //If part is not highlighted update the pixel
+ if ( material._highlighted )
+ {
+ material._highlighted = false;
+
+ dtColorFront = new x3dom.fields.SFColorRGBA(material._diffuseColor.r, material._diffuseColor.g,
+ material._diffuseColor.b, 1.0 - material._transparency);
+ eaColorFront = new x3dom.fields.SFColorRGBA(material._emissiveColor.r, material._emissiveColor.g,
+ material._emissiveColor.b, material._ambientIntensity);
+ ssColorFront = new x3dom.fields.SFColorRGBA(material._specularColor.r, material._specularColor.g,
+ material._specularColor.b, material._shininess);
+
+ dtColorBack = new x3dom.fields.SFColorRGBA(material._backDiffuseColor.r, material._backDiffuseColor.g,
+ material._backDiffuseColor.b, 1.0 - material._backTransparency);
+ eaColorBack = new x3dom.fields.SFColorRGBA(material._backEmissiveColor.r, material._backEmissiveColor.g,
+ material._backEmissiveColor.b, material._backAmbientIntensity);
+ ssColorBack = new x3dom.fields.SFColorRGBA(material._backSpecularColor.r, material._backSpecularColor.g,
+ material._backSpecularColor.b, material._backShininess);
+
+ this.colorMap.setPixel(xFront, yFront, dtColorFront, false);
+ this.emissiveMap.setPixel(xFront, yFront, eaColorFront, false);
+ this.specularMap.setPixel(xFront, yFront, ssColorFront, false);
+
+ this.colorMap.setPixel(xBack, yBack, dtColorBack, false);
+ this.emissiveMap.setPixel(xBack, yBack, eaColorBack, false);
+ this.specularMap.setPixel(xBack, yBack, ssColorBack, true);
+ }
+ }
+ };
+
+
+ /**
+ *
+ * @param color
+ */
+ this.toggleHighlight = function ( color ) {
+ for ( var i=0; i < parts.ids.length; i++ ) {
+ if ( this.multiPart._materials[parts.ids[i]]._highlighted ) {
+ this.unhighlight();
+ } else {
+ this.highlight(color);
+ }
+ }
+ };
+
+
+ /**
+ *
+ * @param color
+ * @param side
+ */
+ this.setColor = function(color, side) {
+ this.setDiffuseColor(color, side);
+ };
+
+
+ /**
+ * Returns the RGB string representation of a color
+ * @returns {String}
+ */
+ this.getColorRGB = function() {
+ var str = this.getColorRGBA();
+
+ var values = str.split(" ");
+
+ return values[0] + " " + values[1] + " " + values[2];
+ };
+
+ /**
+ * Returns the RGBA string representation of a color
+ * @returns {String}
+ */
+ this.getColorRGBA = function() {
+ var x, y;
+
+ //in case of multi select, this function returns the color of the first object
+ var colorRGBA = this.multiPart._originalColor[parts.ids[0]];
+
+ if (this.multiPart._highlightedParts[parts.ids[0]]){
+ colorRGBA = this.multiPart._highlightedParts[parts.ids[0]];
+ } else {
+ x = parts.ids[0] % parts.colorMap.getWidth();
+ y = Math.floor(parts.ids[0] / parts.colorMap.getWidth());
+ colorRGBA = parts.colorMap.getPixel(x, y);
+ }
+
+ return colorRGBA.toString();
+ };
+
+ /**
+ *
+ */
+ this.resetColor = function() {
+
+ var i, partID, pixelIDFront, pixelIDBack, material;
+ var dtColorFront, eaColorFront, ssColorFront;
+ var dtColorBack, eaColorBack, ssColorBack;
+
+ if (ids.length && ids.length > 1) //Multi select
+ {
+ //Get original pixels
+ var dtPixels = parts.colorMap.getPixels();
+ var eaPixels = parts.emissiveMap.getPixels();
+ var ssPixels = parts.specularMap.getPixels();
+
+ for ( i=0; i < parts.ids.length; i++ ) {
+ partID = parts.ids[i];
+ pixelIDFront = partID;
+ pixelIDBack = partID + this.widthTwo;
+
+ material = this.multiPart._materials[partID];
+
+ material.reset();
+
+ if( !material._highlighted )
+ {
+ dtPixels[pixelIDFront] = new x3dom.fields.SFColorRGBA(material._diffuseColor.r, material._diffuseColor.g,
+ material._diffuseColor.b, 1.0 - material._transparency);
+ eaPixels[pixelIDFront] = new x3dom.fields.SFColorRGBA(material._emissiveColor.r, material._emissiveColor.g,
+ material._emissiveColor.b, material._ambientIntensity);
+ ssPixels[pixelIDFront] = new x3dom.fields.SFColorRGBA(material._specularColor.r, material._specularColor.g,
+ material._specularColor.b, material._shininess);
+
+ dtPixels[pixelIDBack] = new x3dom.fields.SFColorRGBA(material._backDiffuseColor.r, material._backDiffuseColor.g,
+ material._backDiffuseColor.b, 1.0 - material._backTransparency);
+ eaPixels[pixelIDBack] = new x3dom.fields.SFColorRGBA(material._backEmissiveColor.r, material._backEmissiveColor.g,
+ material._backEmissiveColor.b, material._backAmbientIntensity);
+ ssPixels[pixelIDBack] = new x3dom.fields.SFColorRGBA(material._backSpecularColor.r, material._backSpecularColor.g,
+ material._backSpecularColor.b, material._backShininess);
+ }
+ }
+
+ this.colorMap.setPixels(dtPixels, false);
+ this.emissiveMap.setPixels(eaPixels, false);
+ this.specularMap.setPixels(ssPixels, true);
+ }
+ else //Single select
+ {
+ partID = parts.ids[0];
+ pixelIDFront = partID;
+ pixelIDBack = partID + this.widthTwo;
+
+ var xFront = pixelIDFront % this.width;
+ var yFront = Math.floor(pixelIDFront / this.width);
+
+ var xBack = pixelIDBack % this.width;
+ var yBack = Math.floor(pixelIDBack / this.width);
+
+ material = this.multiPart._materials[partID];
+
+ material.reset();
+
+ //If part is not highlighted update the pixel
+ if ( !material._highlighted )
+ {
+ dtColorFront = new x3dom.fields.SFColorRGBA(material._diffuseColor.r, material._diffuseColor.g,
+ material._diffuseColor.b, 1.0 - material._transparency);
+ eaColorFront = new x3dom.fields.SFColorRGBA(material._emissiveColor.r, material._emissiveColor.g,
+ material._emissiveColor.b, material._ambientIntensity);
+ ssColorFront = new x3dom.fields.SFColorRGBA(material._specularColor.r, material._specularColor.g,
+ material._specularColor.b, material._shininess);
+
+ dtColorBack = new x3dom.fields.SFColorRGBA(material._backDiffuseColor.r, material._backDiffuseColor.g,
+ material._backDiffuseColor.b, 1.0 - material._backTransparency);
+ eaColorBack = new x3dom.fields.SFColorRGBA(material._backEmissiveColor.r, material._backEmissiveColor.g,
+ material._backEmissiveColor.b, material._backAmbientIntensity);
+ ssColorBack = new x3dom.fields.SFColorRGBA(material._backSpecularColor.r, material._backSpecularColor.g,
+ material._backSpecularColor.b, material._backShininess);
+
+ this.colorMap.setPixel(xFront, yFront, dtColorFront, false);
+ this.emissiveMap.setPixel(xFront, yFront, eaColorFront, false);
+ this.specularMap.setPixel(xFront, yFront, ssColorFront, false);
+
+ this.colorMap.setPixel(xBack, yBack, dtColorBack, false);
+ this.emissiveMap.setPixel(xBack, yBack, eaColorBack, false);
+ this.specularMap.setPixel(xBack, yBack, ssColorBack, true);
+ }
+ }
+ };
+
+ /**
+ *
+ * @param visibility
+ */
+ this.setVisibility = function(visibility) {
+
+ var i, j, x, y, usage, visibleCount, visibilityAsInt;
+
+ if (!(ids.length && ids.length > 1)) {
+ x = parts.ids[0] % parts.colorMap.getWidth();
+ y = Math.floor(parts.ids[0] / parts.colorMap.getWidth());
+
+ var pixel = parts.visibilityMap.getPixel(x, y);
+
+ visibilityAsInt = (visibility) ? 1 : 0;
+
+ if (pixel.r != visibilityAsInt) {
+ pixel.r = visibilityAsInt;
+
+ this.multiPart._partVisibility[parts.ids[0]] = visibility;
+
+ //get used shapes
+ usage = this.multiPart._idMap.mapping[parts.ids[0]].usage;
+
+ //Change the shapes render flag
+ for (j = 0; j < usage.length; j++) {
+ visibleCount = this.multiPart._visiblePartsPerShape[usage[j]];
+ if (visibility && visibleCount.val < visibleCount.max) {
+ visibleCount.val++;
+ } else if (!visibility && visibleCount.val > 0) {
+ visibleCount.val--;
+ }
+
+ if (visibleCount.val) {
+ this.multiPart._inlineNamespace.defMap[usage[j]]._vf.render = true;
+ } else {
+ this.multiPart._inlineNamespace.defMap[usage[j]]._vf.render = false;
+ }
+ }
+ }
+
+ parts.visibilityMap.setPixel(x, y, pixel);
+ this.multiPart.invalidateVolume();
+ }
+ else
+ {
+ var pixels = parts.visibilityMap.getPixels();
+
+ for (i = 0; i < parts.ids.length; i++) {
+
+ visibilityAsInt = (visibility) ? 1 : 0;
+
+ if (pixels[parts.ids[i]].r != visibilityAsInt) {
+ pixels[parts.ids[i]].r = visibilityAsInt;
+
+ this.multiPart._partVisibility[parts.ids[i]] = visibility;
+
+ //get used shapes
+ usage = this.multiPart._idMap.mapping[parts.ids[i]].usage;
+
+ //Change the shapes render flag
+ for (j = 0; j < usage.length; j++) {
+ visibleCount = this.multiPart._visiblePartsPerShape[usage[j]];
+ if (visibility && visibleCount.val < visibleCount.max) {
+ visibleCount.val++;
+ } else if (!visibility && visibleCount.val > 0) {
+ visibleCount.val--;
+ }
+
+ if (visibleCount.val) {
+ this.multiPart._inlineNamespace.defMap[usage[j]]._vf.render = true;
+ } else {
+ this.multiPart._inlineNamespace.defMap[usage[j]]._vf.render = false;
+ }
+ }
+ }
+ }
+
+ parts.visibilityMap.setPixels(pixels);
+ this.multiPart.invalidateVolume();
+ }
+ };
+
+
+ /**
+ * get bounding volume
+ *
+ */
+ this.getVolume = function() {
+
+ var volume;
+ var transmat = this.multiPart.getCurrentTransform();
+
+ if (ids.length && ids.length > 1) //Multi select
+ {
+ volume = new x3dom.fields.BoxVolume();
+
+ for(var i=0; i<parts.ids.length; i++) {
+ volume.extendBounds(this.multiPart._partVolume[parts.ids[i]].min, this.multiPart._partVolume[parts.ids[i]].max);
+ }
+
+ volume.transform(transmat);
+
+ return volume;
+ }
+ else
+ {
+ volume = x3dom.fields.BoxVolume.copy(this.multiPart._partVolume[parts.ids[0]]);
+ volume.transform(transmat);
+ return volume;
+ }
+ };
+
+ /**
+ * Fit the selected Parts to the screen
+ * @param updateCenterOfRotation
+ */
+ this.fit = function (updateCenterOfRotation) {
+
+ var volume = this.getVolume();
+
+ this.multiPart._nameSpace.doc._viewarea.fit(volume.min, volume.max, updateCenterOfRotation);
+ };
+};
+/*
+ * X3DOM JavaScript Library
+ * http://www.x3dom.org
+ *
+ * (C)2009 Fraunhofer IGD, Darmstadt, Germany
+ * Dual licensed under the MIT and GPL
+ *
+ * Based on code originally provided by
+ * Philip Taylor: http://philip.html5.org
+ */
+
+
+x3dom.Properties = function() {
+ this.properties = {};
+};
+
+x3dom.Properties.prototype.setProperty = function(name, value) {
+ x3dom.debug.logInfo("Properties: Setting property '"+ name + "' to value '" + value + "'");
+ this.properties[name] = value;
+};
+
+x3dom.Properties.prototype.getProperty = function(name, def) {
+ if (this.properties[name]) {
+ return this.properties[name]
+ } else {
+ return def;
+ }
+};
+
+x3dom.Properties.prototype.merge = function(other) {
+ for (var attrname in other.properties) {
+ this.properties[attrname] = other.properties[attrname];
+ }
+};
+
+x3dom.Properties.prototype.toString = function() {
+ var str = "";
+ for (var name in this.properties) {
+ str += "Name: " + name + " Value: " + this.properties[name] + "\n";
+ }
+ return str;
+};
+
+/*
+ * X3DOM JavaScript Library
+ * http://www.x3dom.org
+ *
+ * (C)2009 Fraunhofer IGD, Darmstadt, Germany
+ * Dual licensed under the MIT and GPL
+ *
+ * Based on code originally provided by
+ * Philip Taylor: http://philip.html5.org
+ */
+
+x3dom.DoublyLinkedList = function() {
+ this.length = 0;
+ this.first = null;
+ this.last = null;
+};
+
+x3dom.DoublyLinkedList.ListNode = function(point, point_index, normals, colors, texCoords) {
+ this.point = point;
+ this.point_index = point_index;
+ this.normals = normals;
+ this.colors = colors;
+ this.texCoords = texCoords;
+ this.next = null;
+ this.prev = null;
+};
+
+x3dom.DoublyLinkedList.prototype.appendNode = function(node) {
+ if (this.first === null) {
+ node.prev = node;
+ node.next = node;
+ this.first = node;
+ this.last = node;
+ } else {
+ node.prev = this.last;
+ node.next = this.first;
+ this.first.prev = node;
+ this.last.next = node;
+ this.last = node;
+ }
+ this.length++;
+};
+
+x3dom.DoublyLinkedList.prototype.insertAfterNode = function(node, newNode) {
+ newNode.prev = node;
+ newNode.next = node.next;
+ node.next.prev = newNode;
+ node.next = newNode;
+ if (newNode.prev == this.last) {
+ this.last = newNode;
+ }
+ this.length++;
+};
+
+x3dom.DoublyLinkedList.prototype.deleteNode = function(node) {
+ if (this.length > 1) {
+ node.prev.next = node.next;
+ node.next.prev = node.prev;
+ if (node == this.first) {
+ this.first = node.next;
+ }
+ if (node == this.last) {
+ this.last = node.prev;
+ }
+ } else {
+ this.first = null;
+ this.last = null;
+ }
+ node.prev = null;
+ node.next = null;
+ this.length--;
+};
+
+x3dom.DoublyLinkedList.prototype.getNode = function(index) {
+ var node = null;
+ if(index > this.length) {
+ return node;
+ }
+ for(var i = 0; i < this.length; i++) {
+ if(i == 0) {
+ node = this.first;
+ } else {
+ node = node.next;
+ }
+ if(i == index) {
+ return node;
+ }
+ }
+ return null;
+};
+
+x3dom.DoublyLinkedList.prototype.invert = function() {
+ var tmp = null;
+ var node = this.first;
+
+ for(var i = 0; i < this.length; i++) {
+ tmp = node.prev;
+ node.prev = node.next;
+ node.next = tmp;
+ node = node.prev;
+ }
+ tmp = this.first;
+ this.first = this.last;
+ this.last = tmp;
+};
+
+/*
+ * X3DOM JavaScript Library
+ * http://www.x3dom.org
+ *
+ * (C)2009 Fraunhofer IGD, Darmstadt, Germany
+ * Dual licensed under the MIT and GPL
+ *
+ * Based on code originally provided by
+ * Philip Taylor: http://philip.html5.org
+ */
+
+
+x3dom.EarClipping = {
+
+ reversePointDirection: function (linklist, plane) {
+ var l, k;
+ var count = 0;
+ var z = 0;
+ var nodei, nodel, nodek;
+
+ if (linklist.length < 3) {
+ return false;
+ }
+
+ for (var i = 0; i < linklist.length; i++) {
+ l = (i + 1) % linklist.length;
+ k = (i + 2) % linklist.length;
+
+ nodei = linklist.getNode(i);
+ nodel = linklist.getNode(l);
+ nodek = linklist.getNode(k);
+
+ if(plane == 'YZ') {
+ z = (nodel.point.y - nodei.point.y) * (nodek.point.z - nodel.point.z);
+ z -= (nodel.point.z - nodei.point.z) * (nodek.point.y - nodel.point.y);
+ } else if(plane == 'XZ') {
+ z = (nodel.point.z - nodei.point.z) * (nodek.point.x - nodel.point.x);
+ z -= (nodel.point.x - nodei.point.x) * (nodek.point.z - nodel.point.z);
+ } else {
+ z = (nodel.point.x - nodei.point.x) * (nodek.point.y - nodel.point.y);
+ z -= (nodel.point.y - nodei.point.y) * (nodek.point.x - nodel.point.x);
+ }
+
+ if (z < 0) {
+ count--;
+ } else {
+ count++;
+ }
+ }
+
+ if (count < 0) {
+ linklist.invert();
+ return true;
+ }
+ return false;
+ },
+
+ getIndexes: function (linklist) {
+ var node = linklist.first.next;
+ var plane = this.identifyPlane(node.prev.point, node.point, node.next.point);
+
+ var invers = this.reversePointDirection(linklist, plane);
+ var indexes = [];
+ node = linklist.first.next;
+ var next = null;
+ var count = 0;
+
+ var isEar = true;
+
+ while(linklist.length >= 3 && count < 15) {
+ next = node.next;
+ for(var i = 0; i < linklist.length; i++) {
+ if(this.isNotEar(linklist.getNode(i).point, node.prev.point, node.point, node.next.point, plane)) {
+ isEar = false;
+ }
+ }
+
+ if(isEar) {
+ if(this.isKonvex(node.prev.point, node.point, node.next.point, plane)) {
+ indexes.push(node.prev.point_index, node.point_index, node.next.point_index);
+ linklist.deleteNode(node);
+ } else {
+ count++;
+ }
+ }
+
+ node = next;
+ isEar = true;
+ }
+ if(invers){
+ return indexes.reverse();
+ } else {
+ return indexes;
+ }
+ },
+
+ getMultiIndexes: function (linklist) {
+ var node = linklist.first.next;
+ var plane = this.identifyPlane(node.prev.point, node.point, node.next.point);
+ var invers = this.reversePointDirection(linklist, plane);
+
+ var data = {};
+ data.indices = [];
+ data.point = [];
+ data.normals = [];
+ data.colors = [];
+ data.texCoords = [];
+ node = linklist.first.next;
+ var next = null;
+ var count = 0;
+
+ var isEar = true;
+ while(linklist.length >= 3 && count < 15) {
+
+ next = node.next;
+ for(var i = 0; i < linklist.length; i++) {
+
+ if(this.isNotEar(linklist.getNode(i).point, node.prev.point, node.point, node.next.point, plane)) {
+ isEar = false;
+ }
+ }
+ if(isEar) {
+
+ if(this.isKonvex(node.prev.point, node.point, node.next.point, plane)) {
+ data.indices.push(node.prev.point_index, node.point_index, node.next.point_index);
+ data.point.push(node.prev.point,
+ node.point,
+ node.next.point);
+ if(node.normals) {
+ data.normals.push(node.prev.normals,
+ node.normals,
+ node.next.normals);
+
+ }
+ if(node.colors){
+ data.colors.push(node.prev.colors,
+ node.colors,
+ node.next.colors);
+ }
+ if(node.texCoords){
+ data.texCoords.push(node.prev.texCoords,
+ node.texCoords,
+ node.next.texCoords);
+ }
+ linklist.deleteNode(node);
+ } else {
+ count++;
+ }
+ }
+
+ node = next;
+ isEar = true;
+ }
+
+ if(invers){
+ data.indices = data.indices.reverse();
+ data.point = data.point.reverse();
+ data.normals = data.normals.reverse();
+ data.colors = data.colors.reverse();
+ data.texCoords = data.texCoords.reverse();
+ }
+
+ return data;
+ },
+
+ isNotEar: function (ap1, tp1, tp2, tp3, plane) {
+ var b0, b1, b2, b3;
+ var ap1a, ap1b, tp1a, tp1b, tp2a, tp2b, tp3a, tp3b;
+
+ if(plane == 'YZ') {
+ ap1a = ap1.y; ap1b = ap1.z;
+ tp1a = tp1.y; tp1b = tp1.z;
+ tp2a = tp2.y; tp2b = tp2.z;
+ tp3a = tp3.y; tp3b = tp3.z;
+ } else if(plane == 'XZ') {
+ ap1a = ap1.z; ap1b = ap1.x;
+ tp1a = tp1.z; tp1b = tp1.x;
+ tp2a = tp2.z; tp2b = tp2.x;
+ tp3a = tp3.z; tp3b = tp3.x;
+ } else {
+ ap1a = ap1.x; ap1b = ap1.y;
+ tp1a = tp1.x; tp1b = tp1.y;
+ tp2a = tp2.x; tp2b = tp2.y;
+ tp3a = tp3.x; tp3b = tp3.y;
+ }
+
+ b0 = ((tp2a - tp1a) * (tp3b - tp1b) - (tp3a - tp1a) * (tp2b - tp1b));
+ if (b0 != 0) {
+ b1 = (((tp2a - ap1a) * (tp3b - ap1b) - (tp3a - ap1a) * (tp2b - ap1b)) / b0);
+ b2 = (((tp3a - ap1a) * (tp1b - ap1b) - (tp1a - ap1a) * (tp3b - ap1b)) / b0);
+ b3 = 1 - b1 - b2;
+
+ return ((b1 > 0) && (b2 > 0) && (b3 > 0));
+ }
+ else {
+ return false;
+ }
+ },
+
+ isKonvex: function (p, p1, p2, plane) {
+ var pa, pb, p1a, p1b, p2a, p2b;
+ if(plane == 'YZ') {
+ pa = p.y; pb = p.z;
+ p1a = p1.y; p1b = p1.z;
+ p2a = p2.y; p2b = p2.z;
+ } else if(plane == 'XZ') {
+ pa = p.z; pb = p.x;
+ p1a = p1.z; p1b = p1.x;
+ p2a = p2.z; p2b = p2.x;
+ } else {
+ pa = p.x; pb = p.y;
+ p1a = p1.x; p1b = p1.y;
+ p2a = p2.x; p2b = p2.y;
+ }
+
+ var l = ((p1a - pa) * (p2b - pb) - (p1b - pb) * (p2a - pa));
+ return (l >= 0);
+ },
+
+ identifyPlane: function(p1, p2, p3) {
+ var v1x, v1y, v1z;
+ var v2x, v2y, v2z;
+ var v3x, v3y, v3z;
+
+ v1x = p2.x - p1.x; v1y = p2.y - p1.y; v1z = p2.z - p1.z;
+ v2x = p3.x - p1.x; v2y = p3.y - p1.y; v2z = p3.z - p1.z;
+
+ v3x = Math.abs(v1y*v2z - v1z*v2y);
+ v3y = Math.abs(v1z*v2x - v1x*v2z);
+ v3z = Math.abs(v1x*v2y - v1y*v2x);
+
+ var angle = Math.max(v3x, v3y, v3z);
+
+ if(angle == v3x) {
+ return 'YZ';
+ } else if(angle == v3y) {
+ return 'XZ';
+ } else if(angle == v3z) {
+ return 'XY';
+ } else {
+ return 'XZ'; // error
+ }
+ }
+};
+
+/*
+ * X3DOM JavaScript Library
+ * http://www.x3dom.org
+ *
+ * (C)2009 Fraunhofer IGD, Darmstadt, Germany
+ * Dual licensed under the MIT and GPL
+ *
+ * Based on code originally provided by
+ * Philip Taylor: http://philip.html5.org
+ */
+
+/*****************************************************************************
+* Utils class holds utility functions for renderer
+*****************************************************************************/
+x3dom.Utils = {};
+
+x3dom.Utils.maxIndexableCoords = 65535;
+x3dom.Utils.needLineWidth = false; // lineWidth not impl. in IE11
+x3dom.Utils.measurements = [];
+
+
+// http://gent.ilcore.com/2012/06/better-timer-for-javascript.html
+window.performance = window.performance || {};
+performance.now = (function () {
+ return performance.now ||
+ performance.mozNow ||
+ performance.msNow ||
+ performance.oNow ||
+ performance.webkitNow ||
+ function () {
+ return new Date().getTime();
+ };
+})();
+
+x3dom.Utils.startMeasure = function (name) {
+ var uname = name.toUpperCase();
+ if (!x3dom.Utils.measurements[uname]) {
+ if (performance && performance.now) {
+ x3dom.Utils.measurements[uname] = performance.now();
+ } else {
+ x3dom.Utils.measurements[uname] = new Date().getTime();
+ }
+ }
+};
+
+x3dom.Utils.stopMeasure = function (name) {
+ var uname = name.toUpperCase();
+ if (x3dom.Utils.measurements[uname]) {
+ var startTime = x3dom.Utils.measurements[uname];
+ delete x3dom.Utils.measurements[uname];
+ if (performance && performance.now) {
+ return performance.now() - startTime;
+ } else {
+ return new Date().getTime() - startTime;
+ }
+ }
+ return 0;
+};
+
+/*****************************************************************************
+ *
+ *****************************************************************************/
+x3dom.Utils.isNumber = function(n) {
+ return !isNaN(parseFloat(n)) && isFinite(n);
+};
+
+/*****************************************************************************
+*
+*****************************************************************************/
+x3dom.Utils.createTexture2D = function(gl, doc, src, bgnd, crossOrigin, scale, genMipMaps)
+{
+ var texture = gl.createTexture();
+
+ //Create a black 4 pixel texture to prevent 'texture not complete' warning
+ var data = new Uint8Array([0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255]);
+ gl.bindTexture(gl.TEXTURE_2D, texture);
+ gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 2, 2, 0, gl.RGBA, gl.UNSIGNED_BYTE, data);
+ if (genMipMaps) {
+ gl.generateMipmap(gl.TEXTURE_2D);
+ }
+ gl.bindTexture(gl.TEXTURE_2D, null);
+
+ texture.ready = false;
+
+ if (src == null || src == '')
+ return texture;
+
+ var image = new Image();
+
+ switch(crossOrigin.toLowerCase()) {
+ case 'anonymous': {
+ image.crossOrigin = 'anonymous';
+ } break;
+ case 'use-credentials': {
+ image.crossOrigin = 'use-credentials'
+ } break;
+ case 'none': {
+ //this is needed to omit the default case, if default is none, erase this and the default case
+ } break;
+ default: {
+ if(x3dom.Utils.forbiddenBySOP(src)) {
+ image.crossOrigin = 'anonymous';
+ }
+ }
+ }
+
+ image.src = src;
+
+ doc.downloadCount++;
+
+ image.onload = function() {
+ if (scale)
+ image = x3dom.Utils.scaleImage( image );
+
+ if(bgnd == true) {
+ gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true);
+ }
+ gl.bindTexture(gl.TEXTURE_2D, texture);
+ //gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, false);
+ gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image);
+ if (genMipMaps) {
+ gl.generateMipmap(gl.TEXTURE_2D);
+ }
+ gl.bindTexture(gl.TEXTURE_2D, null);
+ if(bgnd == true) {
+ gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, false);
+ }
+
+ //Save image size
+ texture.width = image.width;
+ texture.height = image.height;
+ texture.ready = true;
+
+ doc.downloadCount--;
+ doc.needRender = true;
+ };
+
+ image.onerror = function() {
+ x3dom.debug.logError("[Utils|createTexture2D] Can't load Image: " + src);
+ doc.downloadCount--;
+ };
+
+ return texture;
+};
+
+/*****************************************************************************
+*
+*****************************************************************************/
+x3dom.Utils.createTextureCube = function(gl, doc, src, bgnd, crossOrigin, scale, genMipMaps)
+{
+ var texture = gl.createTexture();
+
+ var faces;
+ if (bgnd) {
+ faces = [gl.TEXTURE_CUBE_MAP_POSITIVE_Z, gl.TEXTURE_CUBE_MAP_NEGATIVE_Z,
+ gl.TEXTURE_CUBE_MAP_POSITIVE_Y, gl.TEXTURE_CUBE_MAP_NEGATIVE_Y,
+ gl.TEXTURE_CUBE_MAP_POSITIVE_X, gl.TEXTURE_CUBE_MAP_NEGATIVE_X];
+ }
+ else
+ {
+ // back, front, bottom, top, left, right
+ faces = [gl.TEXTURE_CUBE_MAP_NEGATIVE_Z, gl.TEXTURE_CUBE_MAP_POSITIVE_Z,
+ gl.TEXTURE_CUBE_MAP_NEGATIVE_Y, gl.TEXTURE_CUBE_MAP_POSITIVE_Y,
+ gl.TEXTURE_CUBE_MAP_NEGATIVE_X, gl.TEXTURE_CUBE_MAP_POSITIVE_X];
+ }
+
+ texture.ready = false;
+ texture.pendingTextureLoads = -1;
+ texture.textureCubeReady = false;
+
+ var width = 0, height = 0;
+
+ for (var i=0; i<faces.length; i++) {
+ var face = faces[i];
+
+ var image = new Image();
+
+ switch(crossOrigin.toLowerCase()) {
+ case 'anonymous': {
+ image.crossOrigin = 'anonymous';
+ } break;
+ case 'use-credentials': {
+ image.crossOrigin = 'use-credentials'
+ } break;
+ case 'none': {
+ //this is needed to omit the default case, if default is none, erase this and the default case
+ } break;
+ default: {
+ if(x3dom.Utils.forbiddenBySOP(src[i])) {
+ image.crossOrigin = 'anonymous';
+ }
+ }
+ }
+
+ texture.pendingTextureLoads++;
+ doc.downloadCount++;
+
+ image.onload = (function(texture, face, image, swap) {
+ return function() {
+ if (width == 0 && height == 0) {
+ width = image.width;
+ height = image.height;
+ }
+ else if (scale && (width != image.width || height != image.height)) {
+ x3dom.debug.logWarning("[Utils|createTextureCube] Rescaling CubeMap images, which are of different size!");
+ image = x3dom.Utils.rescaleImage(image, width, height);
+ }
+
+ gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, swap);
+ gl.bindTexture(gl.TEXTURE_CUBE_MAP, texture);
+ gl.texImage2D(face, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image);
+ gl.bindTexture(gl.TEXTURE_CUBE_MAP, null);
+ gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, false);
+
+ texture.pendingTextureLoads--;
+ doc.downloadCount--;
+
+ if (texture.pendingTextureLoads < 0) {
+ //Save image size also for cube tex
+ texture.width = width;
+ texture.height = height;
+ texture.textureCubeReady = true;
+
+ if (genMipMaps) {
+ gl.bindTexture(gl.TEXTURE_CUBE_MAP, texture);
+ gl.generateMipmap(gl.TEXTURE_CUBE_MAP);
+ gl.bindTexture(gl.TEXTURE_CUBE_MAP, null);
+ }
+
+ x3dom.debug.logInfo("[Utils|createTextureCube] Loading CubeMap finished...");
+ doc.needRender = true;
+ }
+ };
+ })( texture, face, image, bgnd );
+
+ image.onerror = function()
+ {
+ doc.downloadCount--;
+
+ x3dom.debug.logError("[Utils|createTextureCube] Can't load CubeMap!");
+ };
+
+ // backUrl, frontUrl, bottomUrl, topUrl, leftUrl, rightUrl (for bgnd)
+ image.src = src[i];
+ }
+
+ return texture;
+};
+
+/*****************************************************************************
+ * Initialize framebuffer object and associated texture(s)
+ *****************************************************************************/
+x3dom.Utils.initFBO = function(gl, w, h, type, mipMap, needRenderBuf, numMrt) {
+ var tex = gl.createTexture();
+ tex.width = w;
+ tex.height = h;
+
+ gl.bindTexture(gl.TEXTURE_2D, tex);
+ gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, w, h, 0, gl.RGBA, type, null);
+ if (mipMap)
+ gl.generateMipmap(gl.TEXTURE_2D);
+ gl.bindTexture(gl.TEXTURE_2D, null);
+
+ var i, mrts = null;
+
+ if (x3dom.caps.DRAW_BUFFERS && numMrt !== undefined) {
+ mrts = [ tex ];
+
+ for (i=1; i<numMrt; i++) {
+ mrts[i] = gl.createTexture();
+ mrts[i].width = w;
+ mrts[i].height = h;
+
+ gl.bindTexture(gl.TEXTURE_2D, mrts[i]);
+ gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, w, h, 0, gl.RGBA, type, null);
+ if (mipMap)
+ gl.generateMipmap(gl.TEXTURE_2D);
+ gl.bindTexture(gl.TEXTURE_2D, null);
+ }
+ }
+
+ var fbo = gl.createFramebuffer();
+ var rb = null;
+
+ if (needRenderBuf) {
+ rb = gl.createRenderbuffer();
+
+ gl.bindRenderbuffer(gl.RENDERBUFFER, rb);
+ gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_COMPONENT16, w, h);
+ gl.bindRenderbuffer(gl.RENDERBUFFER, null);
+ }
+
+ gl.bindFramebuffer(gl.FRAMEBUFFER, fbo);
+
+ gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, tex, 0);
+ if (x3dom.caps.DRAW_BUFFERS && numMrt !== undefined) {
+ for (i=1; i<numMrt; i++) {
+ gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0 + i, gl.TEXTURE_2D, mrts[i], 0);
+ }
+ }
+ gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, rb);
+
+ var status = gl.checkFramebufferStatus(gl.FRAMEBUFFER);
+ if (status != gl.FRAMEBUFFER_COMPLETE) {
+ x3dom.debug.logWarning("[Utils|InitFBO] FBO-Status: " + status);
+ }
+
+ gl.bindFramebuffer(gl.FRAMEBUFFER, null);
+
+ return {
+ fbo: fbo, rbo: rb,
+ tex: tex, texTargets: mrts,
+ width: w, height: h,
+ type: type, mipMap: mipMap
+ };
+};
+
+/*****************************************************************************
+*
+*****************************************************************************/
+x3dom.Utils.getFileName = function(url)
+{
+ var filename;
+
+ if( url.lastIndexOf("/") > -1 ) {
+ filename = url.substr( url.lastIndexOf("/") + 1 );
+ }
+ else if( url.lastIndexOf("\\") > -1 ) {
+ filename = url.substr( url.lastIndexOf("\\") + 1 );
+ }
+ else {
+ filename = url;
+ }
+
+ return filename;
+};
+
+/*****************************************************************************
+*
+*****************************************************************************/
+x3dom.Utils.findTextureByName = function(texture, name)
+{
+ for ( var i=0; i<texture.length; ++i )
+ {
+ if ( name == texture[i].samplerName )
+ return texture[i];
+ }
+ return false;
+};
+
+/*****************************************************************************
+* Rescale image to given size
+*****************************************************************************/
+x3dom.Utils.rescaleImage = function(image, width, height)
+{
+ var canvas = document.createElement("canvas");
+ canvas.width = width; canvas.height = height;
+ canvas.getContext("2d").drawImage(image,
+ 0, 0, image.width, image.height,
+ 0, 0, canvas.width, canvas.height);
+ return canvas;
+};
+
+/*****************************************************************************
+* Scale image to next best power of two
+*****************************************************************************/
+x3dom.Utils.scaleImage = function(image)
+{
+ if (!x3dom.Utils.isPowerOfTwo(image.width) || !x3dom.Utils.isPowerOfTwo(image.height)) {
+ var canvas = document.createElement("canvas");
+ canvas.width = x3dom.Utils.nextHighestPowerOfTwo(image.width);
+ canvas.height = x3dom.Utils.nextHighestPowerOfTwo(image.height);
+ var ctx = canvas.getContext("2d");
+ ctx.drawImage(image,
+ 0, 0, image.width, image.height,
+ 0, 0, canvas.width, canvas.height);
+ image = canvas;
+ }
+ return image;
+};
+
+
+/*****************************************************************************
+* Check if value is power of two
+*****************************************************************************/
+x3dom.Utils.isPowerOfTwo = function(x)
+{
+ return ((x & (x - 1)) === 0);
+};
+
+
+/*****************************************************************************
+* Return next highest power of two
+*****************************************************************************/
+x3dom.Utils.nextHighestPowerOfTwo = function(x)
+{
+ --x;
+ for (var i = 1; i < 32; i <<= 1) {
+ x = x | x >> i;
+ }
+ return (x + 1);
+};
+
+
+/*****************************************************************************
+* Return next best power of two
+*****************************************************************************/
+x3dom.Utils.nextBestPowerOfTwo = function(x)
+{
+ // use precomputed log(2.0) = 0.693147180559945
+ var log2x = Math.log(x) / 0.693147180559945;
+ return Math.pow(2, Math.round(log2x));
+};
+
+/*****************************************************************************
+* Return data type size in byte
+*****************************************************************************/
+x3dom.Utils.getDataTypeSize = function(type)
+{
+ switch(type)
+ {
+ case "Int8":
+ case "Uint8":
+ return 1;
+ case "Int16":
+ case "Uint16":
+ return 2;
+ case "Int32":
+ case "Uint32":
+ case "Float32":
+ return 4;
+ case "Float64":
+ default:
+ return 8;
+ }
+};
+
+/*****************************************************************************
+ * Return offset multiplier (Uint32 is twice as big as Uint16)
+ *****************************************************************************/
+x3dom.Utils.getOffsetMultiplier = function(indexType, gl)
+{
+ switch(indexType)
+ {
+ case gl.UNSIGNED_SHORT:
+ return 1;
+ case gl.UNSIGNED_INT:
+ return 2;
+ case gl.UNSIGNED_BYTE:
+ return 0.5;
+ default:
+ return 1;
+ }
+};
+
+/*****************************************************************************
+ * Return byte aware offset
+ *****************************************************************************/
+x3dom.Utils.getByteAwareOffset = function(offset, indexType, gl)
+{
+ switch(indexType)
+ {
+ case gl.UNSIGNED_SHORT:
+ return 2 * offset;
+ case gl.UNSIGNED_INT:
+ return 4 * offset;
+ case gl.UNSIGNED_BYTE:
+ return offset;
+ default:
+ return 2 * offset;
+ }
+};
+
+/*****************************************************************************
+* Return this.gl-Type
+*****************************************************************************/
+x3dom.Utils.getVertexAttribType = function(type, gl)
+{
+ var dataType = gl.NONE;
+
+ switch(type)
+ {
+ case "Int8":
+ dataType = gl.BYTE;
+ break;
+ case "Uint8":
+ dataType = gl.UNSIGNED_BYTE;
+ break;
+ case "Int16":
+ dataType = gl.SHORT;
+ break;
+ case "Uint16":
+ dataType = gl.UNSIGNED_SHORT;
+ break;
+ case "Int32":
+ dataType = gl.INT;
+ break;
+ case "Uint32":
+ dataType = gl.UNSIGNED_INT;
+ break;
+ case "Float32":
+ dataType = gl.FLOAT;
+ break;
+ case "Float64":
+ default:
+ x3dom.debug.logError("Can't find this.gl data type for " + type + ", getting FLOAT...");
+ dataType = gl.FLOAT;
+ break;
+ }
+
+ return dataType;
+};
+
+/*****************************************************************************
+* Return TypedArray View
+*****************************************************************************/
+x3dom.Utils.getArrayBufferView = function(type, buffer)
+{
+ var array = null;
+
+ switch(type)
+ {
+ case "Int8":
+ array = new Int8Array(buffer);
+ break;
+ case "Uint8":
+ array = new Uint8Array(buffer);
+ break;
+ case "Int16":
+ array = new Int16Array(buffer);
+ break;
+ case "Uint16":
+ array = new Uint16Array(buffer);
+ break;
+ case "Int32":
+ array = new Int32Array(buffer);
+ break;
+ case "Uint32":
+ array = new Uint32Array(buffer);
+ break;
+ case "Float32":
+ array = new Float32Array(buffer);
+ break;
+ case "Float64":
+ array = new Float64Array(buffer);
+ break;
+ default:
+ x3dom.debug.logError("Can't create typed array view of type " + type + ", trying Float32...");
+ array = new Float32Array(buffer);
+ break;
+ }
+
+ return array;
+};
+
+/*****************************************************************************
+* Checks whether a TypedArray View Type with the given name string is unsigned
+*****************************************************************************/
+x3dom.Utils.isUnsignedType = function (str)
+{
+ return (str == "Uint8" || str == "Uint16" || str == "Uint16" || str == "Uint32");
+};
+
+
+/*****************************************************************************
+* Checks for lighting
+*****************************************************************************/
+x3dom.Utils.checkDirtyLighting = function(viewarea)
+{
+ return (viewarea.getLights().length + viewarea._scene.getNavigationInfo()._vf.headlight);
+};
+
+/*****************************************************************************
+ * Checks for environment
+ *****************************************************************************/
+x3dom.Utils.checkDirtyEnvironment = function(viewarea, shaderProperties)
+{
+ var environment = viewarea._scene.getEnvironment();
+
+ return (shaderProperties.GAMMACORRECTION != environment._vf.gammaCorrectionDefault);
+}
+
+/*****************************************************************************
+* Get GL min filter
+*****************************************************************************/
+x3dom.Utils.minFilterDic = function(gl, minFilter)
+{
+ switch(minFilter.toUpperCase())
+ {
+ case "NEAREST": return gl.NEAREST;
+ case "LINEAR": return gl.LINEAR;
+ case "NEAREST_MIPMAP_NEAREST": return gl.NEAREST_MIPMAP_NEAREST;
+ case "NEAREST_MIPMAP_LINEAR": return gl.NEAREST_MIPMAP_LINEAR;
+ case "LINEAR_MIPMAP_NEAREST": return gl.LINEAR_MIPMAP_NEAREST;
+ case "LINEAR_MIPMAP_LINEAR": return gl.LINEAR_MIPMAP_LINEAR;
+ case "AVG_PIXEL": return gl.LINEAR;
+ case "AVG_PIXEL_AVG_MIPMAP": return gl.LINEAR_MIPMAP_LINEAR;
+ case "AVG_PIXEL_NEAREST_MIPMAP": return gl.LINEAR_MIPMAP_NEAREST;
+ case "DEFAULT": return gl.LINEAR_MIPMAP_LINEAR;
+ case "FASTEST": return gl.NEAREST;
+ case "NEAREST_PIXEL": return gl.NEAREST;
+ case "NEAREST_PIXEL_AVG_MIPMAP": return gl.NEAREST_MIPMAP_LINEAR;
+ case "NEAREST_PIXEL_NEAREST_MIPMAP": return gl.NEAREST_MIPMAP_NEAREST;
+ case "NICEST": return gl.LINEAR_MIPMAP_LINEAR;
+ default: return gl.LINEAR;
+ }
+};
+
+/*****************************************************************************
+* Get GL mag filter
+*****************************************************************************/
+x3dom.Utils.magFilterDic = function(gl, magFilter)
+{
+ switch(magFilter.toUpperCase())
+ {
+ case "NEAREST": return gl.NEAREST;
+ case "LINEAR": return gl.LINEAR;
+ case "AVG_PIXEL": return gl.LINEAR;
+ case "DEFAULT": return gl.LINEAR;
+ case "FASTEST": return gl.NEAREST;
+ case "NEAREST_PIXEL": return gl.NEAREST;
+ case "NICEST": return gl.LINEAR;
+ default: return gl.LINEAR;
+ }
+};
+
+/*****************************************************************************
+* Get GL boundary mode
+*****************************************************************************/
+x3dom.Utils.boundaryModesDic = function(gl, mode)
+{
+ switch(mode.toUpperCase())
+ {
+ case "CLAMP": return gl.CLAMP_TO_EDGE;
+ case "CLAMP_TO_EDGE": return gl.CLAMP_TO_EDGE;
+ case "CLAMP_TO_BOUNDARY": return gl.CLAMP_TO_EDGE;
+ case "MIRRORED_REPEAT": return gl.MIRRORED_REPEAT;
+ case "REPEAT": return gl.REPEAT;
+ default: return gl.REPEAT;
+ }
+};
+
+/*****************************************************************************
+ * Get GL primitive type
+ *****************************************************************************/
+x3dom.Utils.primTypeDic = function(gl, type)
+{
+ switch(type.toUpperCase())
+ {
+ case "POINTS": return gl.POINTS;
+ case "LINES": return gl.LINES;
+ case "LINELOOP": return gl.LINE_LOOP;
+ case "LINESTRIP": return gl.LINE_STRIP;
+ case "TRIANGLES": return gl.TRIANGLES;
+ case "TRIANGLESTRIP": return gl.TRIANGLE_STRIP;
+ case "TRIANGLEFAN": return gl.TRIANGLE_FAN;
+ default: return gl.TRIANGLES;
+ }
+};
+
+/*****************************************************************************
+* Get GL depth function
+*****************************************************************************/
+x3dom.Utils.depthFunc = function(gl, func)
+{
+ switch(func.toUpperCase())
+ {
+ case "NEVER": return gl.NEVER;
+ case "ALWAYS": return gl.ALWAYS;
+ case "LESS": return gl.LESS;
+ case "EQUAL": return gl.EQUAL;
+ case "LEQUAL": return gl.LEQUAL;
+ case "GREATER": return gl.GREATER;
+ case "GEQUAL": return gl.GEQUAL;
+ case "NOTEQUAL": return gl.NOTEQUAL;
+ default: return gl.LEQUAL;
+ }
+};
+
+/*****************************************************************************
+ * Get GL blend function
+ *****************************************************************************/
+x3dom.Utils.blendFunc = function(gl, func)
+{
+ switch(func.toLowerCase())
+ {
+ case "zero": return gl.ZERO;
+ case "one": return gl.ONE;
+ case "dst_color": return gl.DST_COLOR;
+ case "dst_alpha": return gl.DST_ALPHA;
+ case "src_color": return gl.SRC_COLOR;
+ case "src_alpha": return gl.SRC_ALPHA;
+ case "one_minus_dst_color": return gl.ONE_MINUS_DST_COLOR;
+ case "one_minus_dst_alpha": return gl.ONE_MINUS_DST_ALPHA;
+ case "one_minus_src_color": return gl.ONE_MINUS_SRC_COLOR;
+ case "one_minus_src_alpha": return gl.ONE_MINUS_SRC_ALPHA;
+ case "src_alpha_saturate": return gl.SRC_ALPHA_SATURATE;
+ case "constant_color": return gl.CONSTANT_COLOR;
+ case "constant_alpha": return gl.CONSTANT_ALPHA;
+ case "one_minus_constant_color": return gl.ONE_MINUS_CONSTANT_COLOR;
+ case "one_minus_constant_alpha": return gl.ONE_MINUS_CONSTANT_ALPHA;
+ default: return 0;
+ }
+};
+
+/*****************************************************************************
+ * Get GL blend equations
+ *****************************************************************************/
+x3dom.Utils.blendEquation = function(gl, func)
+{
+ switch(func.toLowerCase())
+ {
+ case "func_add": return gl.FUNC_ADD;
+ case "func_subtract": return gl.FUNC_SUBTRACT;
+ case "func_reverse_subtract": return gl.FUNC_REVERSE_SUBTRACT;
+ case "min": return 0; //Not supported yet
+ case "max": return 0; //Not supported yet
+ case "logic_op": return 0; //Not supported yet
+ default: return 0;
+ }
+};
+
+/*****************************************************************************
+*
+*****************************************************************************/
+x3dom.Utils.generateProperties = function (viewarea, shape)
+{
+ var property = {};
+
+ var geometry = shape._cf.geometry.node;
+ var appearance = shape._cf.appearance.node;
+ var texture = appearance ? appearance._cf.texture.node : null;
+ var material = appearance ? appearance._cf.material.node : null;
+ var environment = viewarea._scene.getEnvironment();
+
+ //Check if it's a composed shader
+ if (appearance && appearance._shader &&
+ x3dom.isa(appearance._shader, x3dom.nodeTypes.ComposedShader)) {
+
+ property.CSHADER = appearance._shader._id; //shape._objectID;
+ }
+ else if (geometry) {
+
+ property.CSHADER = -1;
+ property.SOLID = (shape.isSolid()) ? 1 : 0;
+ property.TEXT = (x3dom.isa(geometry, x3dom.nodeTypes.Text)) ? 1 : 0;
+ property.POPGEOMETRY = (x3dom.isa(geometry, x3dom.nodeTypes.PopGeometry)) ? 1 : 0;
+ property.IMAGEGEOMETRY = (x3dom.isa(geometry, x3dom.nodeTypes.ImageGeometry)) ? 1 : 0;
+ property.BINARYGEOMETRY = (x3dom.isa(geometry, x3dom.nodeTypes.BinaryGeometry)) ? 1 : 0;
+ property.IG_PRECISION = (property.IMAGEGEOMETRY) ? geometry.numCoordinateTextures() : 0;
+ property.IG_INDEXED = (property.IMAGEGEOMETRY && geometry.getIndexTexture() != null) ? 1 : 0;
+ property.POINTLINE2D = !geometry.needLighting() ? 1 : 0;
+ property.VERTEXID = (property.BINARYGEOMETRY && geometry._vf.idsPerVertex) ? 1 : 0;
+ property.IS_PARTICLE = (x3dom.isa(geometry, x3dom.nodeTypes.ParticleSet)) ? 1 : 0;
+
+ property.APPMAT = (appearance && (material || property.CSSHADER) ) ? 1 : 0;
+ property.TWOSIDEDMAT = ( property.APPMAT && x3dom.isa(material, x3dom.nodeTypes.TwoSidedMaterial)) ? 1 : 0;
+ property.SEPARATEBACKMAT = ( property.TWOSIDEDMAT && material._vf.separateBackColor) ? 1 : 0;
+ property.SHADOW = (viewarea.getLightsShadow()) ? 1 : 0;
+ property.FOG = (viewarea._scene.getFog()._vf.visibilityRange > 0) ? 1 : 0;
+ property.CSSHADER = (appearance && appearance._shader &&
+ x3dom.isa(appearance._shader, x3dom.nodeTypes.CommonSurfaceShader)) ? 1 : 0;
+ property.LIGHTS = (!property.POINTLINE2D && appearance && shape.isLit() && (material || property.CSSHADER)) ?
+ viewarea.getLights().length + (viewarea._scene.getNavigationInfo()._vf.headlight) : 0;
+ property.TEXTURED = (texture || property.TEXT) ? 1 : 0;
+ property.PIXELTEX = (texture && x3dom.isa(texture, x3dom.nodeTypes.PixelTexture)) ? 1 : 0;
+ property.TEXTRAFO = (appearance && appearance._cf.textureTransform.node) ? 1 : 0;
+ property.DIFFUSEMAP = (property.CSSHADER && appearance._shader.getDiffuseMap()) ? 1 : 0;
+ property.NORMALMAP = (property.CSSHADER && appearance._shader.getNormalMap()) ? 1 : 0;
+ property.SPECMAP = (property.CSSHADER && appearance._shader.getSpecularMap()) ? 1 : 0;
+ property.SHINMAP = (property.CSSHADER && appearance._shader.getShininessMap()) ? 1 : 0;
+ property.DISPLACEMENTMAP = (property.CSSHADER && appearance._shader.getDisplacementMap()) ? 1 : 0;
+ property.DIFFPLACEMENTMAP = (property.CSSHADER && appearance._shader.getDiffuseDisplacementMap()) ? 1 : 0;
+ property.MULTIDIFFALPMAP = (property.VERTEXID && property.CSSHADER && appearance._shader.getMultiDiffuseAlphaMap()) ? 1 : 0;
+ property.MULTIEMIAMBMAP = (property.VERTEXID && property.CSSHADER && appearance._shader.getMultiEmissiveAmbientMap()) ? 1 : 0;
+ property.MULTISPECSHINMAP = (property.VERTEXID && property.CSSHADER && appearance._shader.getMultiSpecularShininessMap()) ? 1 : 0;
+ property.MULTIVISMAP = (property.VERTEXID && property.CSSHADER && appearance._shader.getMultiVisibilityMap()) ? 1 : 0;
+ property.CUBEMAP = (texture && x3dom.isa(texture, x3dom.nodeTypes.X3DEnvironmentTextureNode)) ? 1 : 0;
+ property.BLENDING = (property.TEXT || property.CUBEMAP || (texture && texture._blending)) ? 1 : 0;
+ property.REQUIREBBOX = (geometry._vf.coordType !== undefined && geometry._vf.coordType != "Float32") ? 1 : 0;
+ property.REQUIREBBOXNOR = (geometry._vf.normalType !== undefined && geometry._vf.normalType != "Float32") ? 1 : 0;
+ property.REQUIREBBOXCOL = (geometry._vf.colorType !== undefined && geometry._vf.colorType != "Float32") ? 1 : 0;
+ property.REQUIREBBOXTEX = (geometry._vf.texCoordType !== undefined && geometry._vf.texCoordType != "Float32") ? 1 : 0;
+ property.COLCOMPONENTS = geometry._mesh._numColComponents;
+ property.NORCOMPONENTS = geometry._mesh._numNormComponents;
+ property.POSCOMPONENTS = geometry._mesh._numPosComponents;
+ property.SPHEREMAPPING = (geometry._cf.texCoord !== undefined && geometry._cf.texCoord.node !== null &&
+ geometry._cf.texCoord.node._vf.mode &&
+ geometry._cf.texCoord.node._vf.mode.toLowerCase() == "sphere") ? 1 : 0;
+ property.VERTEXCOLOR = (geometry._mesh._colors[0].length > 0 ||
+ (property.IMAGEGEOMETRY && geometry.getColorTexture()) ||
+ (property.POPGEOMETRY && geometry.hasColor()) ||
+ (geometry._vf.color !== undefined && geometry._vf.color.length > 0)) ? 1 : 0;
+ property.CLIPPLANES = shape._clipPlanes.length;
+
+ property.GAMMACORRECTION = environment._vf.gammaCorrectionDefault;
+ }
+
+ property.toIdentifier = function() {
+ var id = "";
+ for(var p in this) {
+ if(this[p] != this.toIdentifier && this[p] != this.toString) {
+ id += this[p];
+ }
+ }
+ this.id = id;
+ return id;
+ };
+
+ property.toString = function() {
+ var str = "";
+ for(var p in this) {
+ if(this[p] != this.toIdentifier && this[p] != this.toString) {
+ str += p + ": " + this[p] + ", ";
+ }
+ }
+ return str;
+ };
+
+ property.toIdentifier();
+
+ return property;
+};
+
+
+/*****************************************************************************
+* Returns "shader" such that "shader.foo = [1,2,3]" magically sets the
+* appropriate uniform
+*****************************************************************************/
+x3dom.Utils.wrapProgram = function (gl, program, shaderID)
+{
+ var shader = {
+ shaderID: shaderID,
+ program: program
+ };
+
+ shader.bind = function () {
+ gl.useProgram(program);
+ };
+
+ var loc = null;
+ var obj = null;
+ var i, glErr;
+
+ // get uniforms
+ var numUniforms = gl.getProgramParameter(program, gl.ACTIVE_UNIFORMS);
+
+ for (i=0; i < numUniforms; ++i) {
+ try {
+ obj = gl.getActiveUniform(program, i);
+ }
+ catch (eu) {
+ if (!obj) continue;
+ }
+
+ glErr = gl.getError();
+ if (glErr) {
+ x3dom.debug.logError("GL-Error (on searching uniforms): " + glErr);
+ }
+
+ loc = gl.getUniformLocation(program, obj.name);
+
+ switch (obj.type) {
+ case gl.SAMPLER_2D:
+ shader.__defineSetter__(obj.name,
+ (function (loc) { return function (val) { gl.uniform1i(loc, val); }; })(loc));
+ break;
+ case gl.SAMPLER_CUBE:
+ shader.__defineSetter__(obj.name,
+ (function (loc) { return function (val) { gl.uniform1i(loc, val); }; })(loc));
+ break;
+ case gl.BOOL:
+ shader.__defineSetter__(obj.name,
+ (function (loc) { return function (val) { gl.uniform1i(loc, val); }; })(loc));
+ break;
+ case gl.FLOAT:
+ /*
+ * Passing a MFFloat type into uniform.
+ * by Sofiane Benchaa, 2012.
+ *
+ * Based on OpenGL specification.
+ * url: http://www.opengl.org/sdk/docs/man/xhtml/glGetUniformLocation.xml
+ *
+ * excerpt : Except if the last part of name indicates a uniform variable array,
+ * the location of the first element of an array can be retrieved by using the name of the array,
+ * or by using the name appended by "[0]".
+ *
+ * Detecting the float array and extracting its uniform name without the brackets.
+ */
+ if (obj.name.indexOf("[0]") != -1)
+ shader.__defineSetter__(obj.name.substring(0, obj.name.length-3),
+ (function (loc) { return function (val) { gl.uniform1fv(loc, new Float32Array(val)); }; })(loc));
+ else
+ shader.__defineSetter__(obj.name,
+ (function (loc) { return function (val) { gl.uniform1f(loc, val); }; })(loc));
+ break;
+ case gl.FLOAT_VEC2:
+ shader.__defineSetter__(obj.name,
+ (function (loc) { return function (val) { gl.uniform2f(loc, val[0], val[1]); }; })(loc));
+ break;
+ case gl.FLOAT_VEC3:
+ /* Passing arrays of vec3. see above.*/
+ if (obj.name.indexOf("[0]") != -1)
+ shader.__defineSetter__(obj.name.substring(0, obj.name.length-3),
+ (function (loc) { return function (val) { gl.uniform3fv(loc, new Float32Array(val)); }; })(loc));
+ else
+ shader.__defineSetter__(obj.name,
+ (function (loc) { return function (val) { gl.uniform3f(loc, val[0], val[1], val[2]); }; })(loc));
+ break;
+ case gl.FLOAT_VEC4:
+ shader.__defineSetter__(obj.name,
+ (function (loc) { return function (val) { gl.uniform4f(loc, val[0], val[1], val[2], val[3]); }; })(loc));
+ break;
+ case gl.FLOAT_MAT2:
+ shader.__defineSetter__(obj.name,
+ (function (loc) { return function (val) { gl.uniformMatrix2fv(loc, false, new Float32Array(val)); }; })(loc));
+ break;
+ case gl.FLOAT_MAT3:
+ shader.__defineSetter__(obj.name,
+ (function (loc) { return function (val) { gl.uniformMatrix3fv(loc, false, new Float32Array(val)); }; })(loc));
+ break;
+ case gl.FLOAT_MAT4:
+ shader.__defineSetter__(obj.name,
+ (function (loc) { return function (val) { gl.uniformMatrix4fv(loc, false, new Float32Array(val)); }; })(loc));
+ break;
+ case gl.INT:
+ shader.__defineSetter__(obj.name,
+ (function (loc) { return function (val) { gl.uniform1i(loc, val); }; }) (loc));
+ break;
+ default:
+ x3dom.debug.logWarning('GLSL program variable '+obj.name+' has unknown type '+obj.type);
+ }
+ }
+
+ // get attributes
+ var numAttribs = gl.getProgramParameter(program, gl.ACTIVE_ATTRIBUTES);
+
+ for (i=0; i < numAttribs; ++i) {
+ try {
+ obj = gl.getActiveAttrib(program, i);
+ }
+ catch (ea) {
+ if (!obj) continue;
+ }
+
+ glErr = gl.getError();
+ if (glErr) {
+ x3dom.debug.logError("GL-Error (on searching attributes): " + glErr);
+ }
+
+ loc = gl.getAttribLocation(program, obj.name);
+ shader[obj.name] = loc;
+ }
+
+ return shader;
+};
+
+
+/**
+ * Matches a given URI with document.location. If domain, port and protocol are the same SOP won't forbid access to the resource.
+ * @param {String} uri_string
+ * @returns {boolean}
+ */
+x3dom.Utils.forbiddenBySOP = function (uri_string) {
+
+ uri_string = uri_string.toLowerCase();
+ // scheme ":" hier-part [ "?" query ] [ "#" fragment ]
+ var Scheme_AuthorityPQF = uri_string.split('//'); //Scheme and AuthorityPathQueryFragment
+ var Scheme;
+ var AuthorityPQF;
+ var Authority;
+ var UserInfo_HostPort;
+ var HostPort;
+ var Host_Port;
+ var Port;
+ var Host;
+ var originPort = document.location.port === "" ? "80" : document.location.port;
+
+ if (Scheme_AuthorityPQF.length === 2) { // if there is '//' authority is given;
+ Scheme = Scheme_AuthorityPQF[0];
+ AuthorityPQF = Scheme_AuthorityPQF[1];
+
+ /*
+ * The authority component is preceded by a double slash ("//") and is
+ * terminated by the next slash ("/"), question mark ("?"), or number
+ * sign ("#") character, or by the end of the URI.
+ */
+ Authority = AuthorityPQF.split('/')[0].split('?')[0].split('#')[0];
+
+ //authority = [ userinfo "@" ] host [ ":" port ]
+ UserInfo_HostPort = Authority.split('@');
+ if (UserInfo_HostPort.length === 1) { //No Userinfo given
+ HostPort = UserInfo_HostPort[0];
+ } else {
+ HostPort = UserInfo_HostPort[1];
+ }
+
+ Host_Port = HostPort.split(':');
+ Host = Host_Port[0];
+ Port = Host_Port[1];
+ } // else will return false for an invalid URL or URL without authority
+
+ Port = Port || "80";
+ Host = Host || document.location.host;
+ Scheme = Scheme || document.location.protocol;
+ return !(Port === originPort && Host === document.location.host && Scheme === document.location.protocol);
+};
+
+/*
+ * X3DOM JavaScript Library
+ * http://www.x3dom.org
+ *
+ * (C)2009 Fraunhofer IGD, Darmstadt, Germany
+ * Dual licensed under the MIT and GPL
+ *
+ * Based on code originally provided by
+ * Philip Taylor: http://philip.html5.org
+ */
+
+/**
+ * States namespace
+ */
+x3dom.States = function (x3dElem) {
+ var that = this;
+ this.active = false;
+
+ this.viewer = document.createElement('div');
+ this.viewer.id = 'x3dom-state-viewer';
+
+ var title = document.createElement('div');
+ title.className = 'x3dom-states-head';
+ title.appendChild(document.createTextNode('x3dom'));
+
+ var subTitle = document.createElement('span');
+ subTitle.className = 'x3dom-states-head2';
+ subTitle.appendChild(document.createTextNode('stats'));
+ title.appendChild(subTitle);
+
+ this.renderMode = document.createElement('div');
+ this.renderMode.className = 'x3dom-states-rendermode-hardware';
+
+ this.measureList = document.createElement('ul');
+ this.measureList.className = 'x3dom-states-list';
+
+ this.infoList = document.createElement('ul');
+ this.infoList.className = 'x3dom-states-list';
+
+ //this.viewer.appendChild(title);
+ this.viewer.appendChild(this.renderMode);
+ this.viewer.appendChild(this.measureList);
+ this.viewer.appendChild(this.infoList);
+
+ /**
+ * Disable the context menu
+ */
+ this.disableContextMenu = function (e) {
+ e.preventDefault();
+ e.stopPropagation();
+ e.returnValue = false;
+ return false;
+ };
+
+ /**
+ * Add a seperator for thousands to the string
+ */
+ this.thousandSeperator = function (value) {
+ return value.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
+ };
+
+ /**
+ * Return numerical value to fixed length
+ */
+ this.toFixed = function (value) {
+ var fixed = (value < 1) ? 2 : (value < 10) ? 2 : 2;
+ return value.toFixed(fixed);
+ };
+
+ /**
+ * Update the states.
+ */
+ this.update = function () {
+ if (!x3dElem.runtime && this.updateMethodID !== undefined) {
+ clearInterval(this.updateMethodID);
+ return;
+ }
+
+ var infos = x3dElem.runtime.states.infos;
+ var measurements = x3dElem.runtime.states.measurements;
+
+ var renderMode = x3dom.caps.RENDERMODE;
+
+ if ( renderMode == "HARDWARE" ) {
+ this.renderMode.innerHTML = "Hardware-Rendering";
+ this.renderMode.className = 'x3dom-states-rendermode-hardware';
+ } else if ( renderMode == "SOFTWARE" ) {
+ this.renderMode.innerHTML = "Software-Rendering";
+ this.renderMode.className = 'x3dom-states-rendermode-software';
+ }
+
+
+ //Clear measure list
+ this.measureList.innerHTML = "";
+
+ //Create list items
+ for (var m in measurements) {
+ infoItem = document.createElement('li');
+ infoItem.className = 'x3dom-states-item';
+
+ infoTitle = document.createElement('div');
+ infoTitle.className = 'x3dom-states-item-title';
+ infoTitle.appendChild(document.createTextNode(m));
+
+ infoValue = document.createElement('div');
+ infoValue.className = 'x3dom-states-item-value';
+ infoValue.appendChild(document.createTextNode(this.toFixed(measurements[m])));
+
+ infoItem.appendChild(infoTitle);
+ infoItem.appendChild(infoValue);
+
+ this.measureList.appendChild(infoItem);
+ }
+
+ //Clear info list
+ this.infoList.innerHTML = "";
+
+ //Create list items
+ for (var i in infos) {
+ var infoItem = document.createElement('li');
+ infoItem.className = 'x3dom-states-item';
+
+ var infoTitle = document.createElement('div');
+ infoTitle.className = 'x3dom-states-item-title';
+ infoTitle.appendChild(document.createTextNode(i));
+
+ var infoValue = document.createElement('div');
+ infoValue.className = 'x3dom-states-item-value';
+ infoValue.appendChild(document.createTextNode(this.thousandSeperator(infos[i])));
+
+ infoItem.appendChild(infoTitle);
+ infoItem.appendChild(infoValue);
+
+ this.infoList.appendChild(infoItem);
+ }
+ };
+
+ this.updateMethodID = window.setInterval(function () {
+ that.update();
+ }, 1000);
+
+ this.viewer.addEventListener("contextmenu", that.disableContextMenu);
+};
+
+/**
+ * Display the states
+ */
+x3dom.States.prototype.display = function (value) {
+ this.active = (value !== undefined) ? value : !this.active;
+ this.viewer.style.display = (this.active) ? "block" : "none";
+};
+
+/*
+ * X3DOM JavaScript Library
+ * http://www.x3dom.org
+ *
+ * (C)2009 Fraunhofer IGD, Darmstadt, Germany
+ * Dual licensed under the MIT and GPL
+ *
+ * Based on code originally provided by
+ * Philip Taylor: http://philip.html5.org
+ */
+
+/**
+ * Manage all the GL-States and try to reduce the state changes
+ */
+x3dom.StateManager = function (ctx3d)
+{
+ //Our GL-Context
+ this.gl = ctx3d;
+
+ //Hold all the active states
+ this.states = [];
+
+ //Initialize States
+ this.initStates();
+};
+
+/*
+ * Initialize States
+ */
+x3dom.StateManager.prototype.initStates = function ()
+{
+ //Initialize Shader states
+ this.states['shaderID'] = null;
+
+ //Initialize Framebuffer-Operation states
+ this.states['colorMask'] = {red: null, green: null, blue: null, alpha: null};
+ this.states['depthMask'] = null;
+ this.states['stencilMask'] = null;
+
+ //Initialize Rasterization states
+ this.states['cullFace'] = null;
+ this.states['frontFace'] = null;
+ this.states['lineWidth'] = null;
+
+ //Initialize Per-Fragment-Operation states
+ this.states['blendColor'] = {red: null, green: null, blue: null, alpha: null};
+ this.states['blendEquation'] = null;
+ this.states['blendEquationSeparate'] = {modeRGB: null, modeAlpha: null};
+ this.states['blendFunc'] = {sfactor: null, dfactor: null};
+ this.states['blendFuncSeparate'] = {srcRGB: null, dstRGB: null, srcAlpha: null, dstAlpha: null};
+ this.states['depthFunc'] = null;
+
+ //Initialize View and Clip states
+ this.states['viewport'] = {x: null, y: null, width: null, height: null};
+ this.states['depthRange'] = {zNear: null, zFar: null};
+
+ //TODO more states (e.g. stencil, texture, ...)
+};
+
+/*
+ * Only bind program if different (returns true if changed)
+ */
+x3dom.StateManager.prototype.useProgram = function (shader)
+{
+ if (this.states['shaderID'] != shader.shaderID)
+ {
+ this.gl.useProgram(shader.program);
+ this.states['shaderID'] = shader.shaderID;
+ return true;
+ }
+ return false;
+};
+
+/*
+ * Unset active program for clean init state
+ */
+x3dom.StateManager.prototype.unsetProgram = function ()
+{
+ this.states['shaderID'] = null;
+};
+
+/*
+ * Enable GL capabilities
+ */
+x3dom.StateManager.prototype.enable = function (cap)
+{
+ if (this.states[cap] !== true)
+ {
+ this.gl.enable(cap);
+ this.states[cap] = true;
+ }
+};
+
+/*
+ * Disable GL capabilities
+ */
+x3dom.StateManager.prototype.disable = function (cap)
+{
+ if (this.states[cap] !== false)
+ {
+ this.gl.disable(cap);
+ this.states[cap] = false;
+ }
+};
+
+/*
+ * Enable and disable writing of frame buffer color components
+ */
+x3dom.StateManager.prototype.colorMask = function (red, green, blue, alpha)
+{
+ if (this.states['colorMask'].red != red ||
+ this.states['colorMask'].green != green ||
+ this.states['colorMask'].blue != blue ||
+ this.states['colorMask'].alpha != alpha)
+ {
+ this.gl.colorMask(red, green, blue, alpha);
+ this.states['colorMask'].red = red;
+ this.states['colorMask'].green = green;
+ this.states['colorMask'].blue = blue;
+ this.states['colorMask'].alpha = alpha;
+ }
+};
+
+/*
+ * Sets whether or not you can write to the depth buffer.
+ */
+x3dom.StateManager.prototype.depthMask = function (flag)
+{
+ if (this.states['depthMask'] != flag)
+ {
+ this.gl.depthMask(flag);
+ this.states['depthMask'] = flag;
+ }
+};
+
+/*
+ * Control the front and back writing of individual bits in the stencil planes
+ */
+x3dom.StateManager.prototype.stencilMask = function (mask)
+{
+ if (this.states['stencilMask'] != mask)
+ {
+ this.gl.stencilMask(mask);
+ this.states['stencilMask'] = mask;
+ }
+};
+
+/*
+ * Specify whether front- or back-facing facets can be culled
+ */
+x3dom.StateManager.prototype.cullFace = function (mode)
+{
+ if (this.states['cullFace'] != mode)
+ {
+ this.gl.cullFace(mode);
+ this.states['cullFace'] = mode;
+ }
+};
+
+/*
+ * Define front- and back-facing polygons
+ */
+x3dom.StateManager.prototype.frontFace = function (mode)
+{
+ if (this.states['frontFace'] != mode)
+ {
+ this.gl.frontFace(mode);
+ this.states['frontFace'] = mode;
+ }
+};
+
+/*
+ * Specify the width of rasterized lines
+ */
+x3dom.StateManager.prototype.lineWidth = function (width)
+{
+ width = (width <= 1) ? 1 : width;
+
+ if (this.states['lineWidth'] != width)
+ {
+ this.gl.lineWidth(width);
+ this.states['lineWidth'] = width;
+ }
+};
+
+/*
+ * Set the blend color
+ */
+x3dom.StateManager.prototype.blendColor = function (red, green, blue, alpha)
+{
+ if (this.states['blendColor'].red != red ||
+ this.states['blendColor'].green != green ||
+ this.states['blendColor'].blue != blue ||
+ this.states['blendColor'].alpha != alpha)
+ {
+ this.gl.blendColor(red, green, blue, alpha);
+ this.states['blendColor'].red = red;
+ this.states['blendColor'].green = green;
+ this.states['blendColor'].blue = blue;
+ this.states['blendColor'].alpha = alpha;
+ }
+};
+
+/*
+ * Specify the equation used for both the RGB blend equation and the Alpha blend equation
+ */
+x3dom.StateManager.prototype.blendEquation = function (mode)
+{
+ if (mode && this.states['blendEquation'] != mode)
+ {
+ this.gl.blendEquation(mode);
+ this.states['blendEquation'] = mode;
+ }
+};
+
+/*
+ * set the RGB blend equation and the alpha blend equation separately
+ */
+x3dom.StateManager.prototype.blendEquationSeparate = function (modeRGB, modeAlpha)
+{
+ if (this.states['blendEquationSeparate'].modeRGB != modeRGB ||
+ this.states['blendEquationSeparate'].modeAlpha != modeAlpha)
+ {
+ this.gl.blendEquationSeparate(modeRGB, modeAlpha);
+ this.states['blendEquationSeparate'].modeRGB = modeRGB;
+ this.states['blendEquationSeparate'].modeAlpha = modeAlpha;
+ }
+};
+
+/*
+ * Specify pixel arithmetic
+ */
+x3dom.StateManager.prototype.blendFunc = function (sfactor, dfactor)
+{
+ if (this.states['blendFunc'].sfactor != sfactor ||
+ this.states['blendFunc'].dfactor != dfactor)
+ {
+ this.gl.blendFunc(sfactor, dfactor);
+ this.states['blendFunc'].sfactor = sfactor;
+ this.states['blendFunc'].dfactor = dfactor;
+ }
+};
+
+/*
+ * Specify pixel arithmetic for RGB and alpha components separately
+ */
+x3dom.StateManager.prototype.blendFuncSeparate = function (srcRGB, dstRGB, srcAlpha, dstAlpha)
+{
+ if (this.states['blendFuncSeparate'].srcRGB != srcRGB ||
+ this.states['blendFuncSeparate'].dstRGB != dstRGB ||
+ this.states['blendFuncSeparate'].srcAlpha != srcAlpha ||
+ this.states['blendFuncSeparate'].dstAlpha != dstAlpha)
+ {
+ this.gl.blendFuncSeparate(srcRGB, dstRGB, srcAlpha, dstAlpha);
+ this.states['blendFuncSeparate'].srcRGB = srcRGB;
+ this.states['blendFuncSeparate'].dstRGB = dstRGB;
+ this.states['blendFuncSeparate'].srcAlpha = srcAlpha;
+ this.states['blendFuncSeparate'].dstAlpha = dstAlpha;
+ }
+};
+
+/*
+ * Specify the value used for depth buffer comparisons
+ */
+x3dom.StateManager.prototype.depthFunc = function (func)
+{
+ if (this.states['depthFunc'] != func)
+ {
+ this.gl.depthFunc(func);
+ this.states['depthFunc'] = func;
+ }
+};
+
+/*
+ * Specify the value used for depth buffer comparisons
+ */
+x3dom.StateManager.prototype.depthRange = function (zNear, zFar)
+{
+ if (zNear < 0 || zFar < 0 || zNear > zFar)
+ {
+ return; // do noting and leave default values
+ }
+
+ zNear = (zNear > 1) ? 1 : zNear;
+ zFar = (zFar > 1) ? 1 : zFar;
+
+ if (this.states['depthRange'].zNear != zNear || this.states['depthRange'].zFar != zFar)
+ {
+ this.gl.depthRange(zNear, zFar);
+ this.states['depthRange'].zNear = zNear;
+ this.states['depthRange'].zFar = zFar;
+ }
+};
+
+/*
+ * Set the viewport
+ */
+x3dom.StateManager.prototype.viewport = function (x, y, width, height)
+{
+ if (this.states['viewport'].x != x ||
+ this.states['viewport'].y != y ||
+ this.states['viewport'].width != width ||
+ this.states['viewport'].height != height)
+ {
+ this.gl.viewport(x, y, width, height);
+ this.states['viewport'].x = x;
+ this.states['viewport'].y = y;
+ this.states['viewport'].width = width;
+ this.states['viewport'].height = height;
+ }
+};
+
+/*
+ * Bind a framebuffer to a framebuffer target
+ */
+x3dom.StateManager.prototype.bindFramebuffer = function (target, framebuffer)
+{
+ this.gl.bindFramebuffer(target, framebuffer);
+ this.initStates();
+};
+
+/*
+ * X3DOM JavaScript Library
+ * http://www.x3dom.org
+ *
+ * (C)2009 Fraunhofer IGD, Darmstadt, Germany
+ * Dual licensed under the MIT and GPL
+ *
+ * Based on code originally provided by
+ * Philip Taylor: http://philip.html5.org
+ */
+
+
+/** used from within gfx_webgl.js */
+x3dom.BinaryContainerLoader = {
+ outOfMemory: false, // try to prevent browser crashes
+
+ checkError: function(gl) {
+ var glErr = gl.getError();
+ if (glErr) {
+ if (glErr == gl.OUT_OF_MEMORY) {
+ this.outOfMemory = true;
+ x3dom.debug.logError("GL-Error " + glErr + " on loading binary container (out of memory).");
+ console.error("WebGL: OUT_OF_MEMORY");
+ }
+ else {
+ x3dom.debug.logError("GL-Error " + glErr + " on loading binary container.");
+ }
+ }
+ }
+};
+
+
+/** setup/download binary geometry */
+x3dom.BinaryContainerLoader.setupBinGeo = function(shape, sp, gl, viewarea, currContext)
+{
+ if (this.outOfMemory) {
+ return;
+ }
+
+ var t00 = new Date().getTime();
+ var that = this;
+
+ var binGeo = shape._cf.geometry.node;
+
+ // 0 := no BG, 1 := indexed BG, -1 := non-indexed BG
+ shape._webgl.binaryGeometry = -1;
+
+ shape._webgl.internalDownloadCount = ((binGeo._vf.index.length > 0) ? 1 : 0) +
+ ((binGeo._hasStrideOffset && binGeo._vf.coord.length > 0) ? 1 : 0) +
+ ((!binGeo._hasStrideOffset && binGeo._vf.coord.length > 0) ? 1 : 0) +
+ ((!binGeo._hasStrideOffset && binGeo._vf.normal.length > 0) ? 1 : 0) +
+ ((!binGeo._hasStrideOffset && binGeo._vf.texCoord.length > 0) ? 1 : 0) +
+ ((!binGeo._hasStrideOffset && binGeo._vf.color.length > 0) ? 1 : 0);
+
+ var createTriangleSoup = (binGeo._vf.normalPerVertex == false) ||
+ ((binGeo._vf.index.length > 0) && (binGeo._vf.indexType == "Int32" ||
+ (binGeo._vf.indexType == "Uint32" && !x3dom.caps.INDEX_UINT)));
+
+ shape._webgl.makeSeparateTris = {
+ index: null,
+ coord: null,
+ normal: null,
+ texCoord: null,
+ color: null,
+
+ pushBuffer: function(name, buf) {
+ this[name] = buf;
+
+ if (--shape._webgl.internalDownloadCount == 0) {
+ if (this.coord)
+ this.createMesh();
+ shape._nameSpace.doc.needRender = true;
+ }
+ if (--shape._nameSpace.doc.downloadCount == 0)
+ shape._nameSpace.doc.needRender = true;
+ },
+
+ createMesh: function() {
+ var geoNode = binGeo;
+
+ if (geoNode._hasStrideOffset) {
+ x3dom.debug.logError(geoNode._vf.indexType +
+ " index type and per-face normals not supported for interleaved arrays.");
+ return;
+ }
+
+ for (var k=0; k<shape._webgl.primType.length; k++) {
+ if (shape._webgl.primType[k] == gl.TRIANGLE_STRIP) {
+ x3dom.debug.logError("makeSeparateTris: triangle strips not yet supported for per-face normals.");
+ return;
+ }
+ }
+
+ var attribTypeStr = geoNode._vf.coordType;
+ shape._webgl.coordType = x3dom.Utils.getVertexAttribType(attribTypeStr, gl);
+
+ // remap vertex data
+ var bgCenter, bgSize, bgPrecisionMax;
+
+ if (shape._webgl.coordType != gl.FLOAT)
+ {
+ if (geoNode._mesh._numPosComponents == 4 &&
+ x3dom.Utils.isUnsignedType(geoNode._vf.coordType))
+ bgCenter = x3dom.fields.SFVec3f.copy(geoNode.getMin());
+ else
+ bgCenter = x3dom.fields.SFVec3f.copy(geoNode._vf.position);
+
+ bgSize = x3dom.fields.SFVec3f.copy(geoNode._vf.size);
+ bgPrecisionMax = geoNode.getPrecisionMax('coordType');
+ }
+ else
+ {
+ bgCenter = new x3dom.fields.SFVec3f(0, 0, 0);
+ bgSize = new x3dom.fields.SFVec3f(1, 1, 1);
+ bgPrecisionMax = 1.0;
+ }
+
+ // check types
+ var dataLen = shape._coordStrideOffset[0] / x3dom.Utils.getDataTypeSize(geoNode._vf.coordType);
+ dataLen = (dataLen == 0) ? 3 : dataLen;
+
+ x3dom.debug.logWarning("makeSeparateTris.createMesh called with coord length " + dataLen);
+
+ if (this.color && dataLen != shape._colorStrideOffset[0] / x3dom.Utils.getDataTypeSize(geoNode._vf.colorType))
+ {
+ this.color = null;
+ x3dom.debug.logWarning("Color format not supported.");
+ }
+
+ var texDataLen = this.texCoord ? (shape._texCoordStrideOffset[0] /
+ x3dom.Utils.getDataTypeSize(geoNode._vf.texCoordType)) : 0;
+
+ // set data types
+ //geoNode._vf.coordType = "Float32";
+ geoNode._vf.normalType = "Float32";
+
+ //shape._webgl.coordType = gl.FLOAT;
+ shape._webgl.normalType = gl.FLOAT;
+
+ //geoNode._mesh._numPosComponents = 3;
+ geoNode._mesh._numNormComponents = 3;
+
+ //shape._coordStrideOffset = [0, 0];
+ shape._normalStrideOffset = [0, 0];
+
+ // create non-indexed mesh
+ var posBuf = [], normBuf = [], texcBuf = [], colBuf = [];
+ var i, j, l, n = this.index ? (this.index.length - 2) : (this.coord.length / 3 - 2);
+
+ for (i=0; i<n; i+=3)
+ {
+ j = dataLen * (this.index ? this.index[i] : i);
+ var p0 = new x3dom.fields.SFVec3f(bgSize.x * this.coord[j ] / bgPrecisionMax,
+ bgSize.y * this.coord[j+1] / bgPrecisionMax,
+ bgSize.z * this.coord[j+2] / bgPrecisionMax);
+ // offset irrelevant for normal calculation
+ //p0 = bgCenter.add(p0);
+
+ posBuf.push(this.coord[j ]);
+ posBuf.push(this.coord[j+1]);
+ posBuf.push(this.coord[j+2]);
+ if (dataLen > 3) posBuf.push(this.coord[j+3]);
+
+ if (this.color) {
+ colBuf.push(this.color[j ]);
+ colBuf.push(this.color[j+1]);
+ colBuf.push(this.color[j+2]);
+ if (dataLen > 3) colBuf.push(this.color[j+3]);
+ }
+
+ if (this.texCoord) {
+ l = texDataLen * (this.index ? this.index[i] : i);
+
+ texcBuf.push(this.texCoord[l ]);
+ texcBuf.push(this.texCoord[l+1]);
+ if (texDataLen > 3) {
+ texcBuf.push(this.texCoord[l+2]);
+ texcBuf.push(this.texCoord[l+3]);
+ }
+ }
+
+ j = dataLen * (this.index ? this.index[i+1] : i+1);
+ var p1 = new x3dom.fields.SFVec3f(bgSize.x * this.coord[j ] / bgPrecisionMax,
+ bgSize.y * this.coord[j+1] / bgPrecisionMax,
+ bgSize.z * this.coord[j+2] / bgPrecisionMax);
+ //p1 = bgCenter.add(p1);
+
+ posBuf.push(this.coord[j ]);
+ posBuf.push(this.coord[j+1]);
+ posBuf.push(this.coord[j+2]);
+ if (dataLen > 3) posBuf.push(this.coord[j+3]);
+
+ if (this.color) {
+ colBuf.push(this.color[j ]);
+ colBuf.push(this.color[j+1]);
+ colBuf.push(this.color[j+2]);
+ if (dataLen > 3) colBuf.push(this.color[j+3]);
+ }
+
+ if (this.texCoord) {
+ l = texDataLen * (this.index ? this.index[i+1] : i+1);
+
+ texcBuf.push(this.texCoord[l ]);
+ texcBuf.push(this.texCoord[l+1]);
+ if (texDataLen > 3) {
+ texcBuf.push(this.texCoord[l+2]);
+ texcBuf.push(this.texCoord[l+3]);
+ }
+ }
+
+ j = dataLen * (this.index ? this.index[i+2] : i+2);
+ var p2 = new x3dom.fields.SFVec3f(bgSize.x * this.coord[j ] / bgPrecisionMax,
+ bgSize.y * this.coord[j+1] / bgPrecisionMax,
+ bgSize.z * this.coord[j+2] / bgPrecisionMax);
+ //p2 = bgCenter.add(p2);
+
+ posBuf.push(this.coord[j ]);
+ posBuf.push(this.coord[j+1]);
+ posBuf.push(this.coord[j+2]);
+ if (dataLen > 3) posBuf.push(this.coord[j+3]);
+
+ if (this.color) {
+ colBuf.push(this.color[j ]);
+ colBuf.push(this.color[j+1]);
+ colBuf.push(this.color[j+2]);
+ if (dataLen > 3) colBuf.push(this.color[j+3]);
+ }
+
+ if (this.texCoord) {
+ l = texDataLen * (this.index ? this.index[i+2] : i+2);
+
+ texcBuf.push(this.texCoord[l ]);
+ texcBuf.push(this.texCoord[l+1]);
+ if (texDataLen > 3) {
+ texcBuf.push(this.texCoord[l+2]);
+ texcBuf.push(this.texCoord[l+3]);
+ }
+ }
+
+ var a = p0.subtract(p1);
+ var b = p1.subtract(p2);
+ var norm = a.cross(b).normalize();
+
+ for (j=0; j<3; j++) {
+ normBuf.push(norm.x);
+ normBuf.push(norm.y);
+ normBuf.push(norm.z);
+ }
+ }
+
+ // coordinates
+ var buffer = gl.createBuffer();
+ shape._webgl.buffers[1] = buffer;
+
+ gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
+ gl.bufferData(gl.ARRAY_BUFFER,
+ x3dom.Utils.getArrayBufferView(geoNode._vf.coordType, posBuf), gl.STATIC_DRAW);
+
+ gl.vertexAttribPointer(sp.position, geoNode._mesh._numPosComponents,
+ shape._webgl.coordType, false,
+ shape._coordStrideOffset[0], shape._coordStrideOffset[1]);
+ gl.enableVertexAttribArray(sp.position);
+
+ // normals
+ buffer = gl.createBuffer();
+ shape._webgl.buffers[2] = buffer;
+
+ gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
+ gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(normBuf), gl.STATIC_DRAW);
+
+ gl.vertexAttribPointer(sp.normal, geoNode._mesh._numNormComponents,
+ shape._webgl.normalType, false,
+ shape._normalStrideOffset[0], shape._normalStrideOffset[1]);
+ gl.enableVertexAttribArray(sp.normal);
+
+ // tex coords
+ if (this.texCoord)
+ {
+ buffer = gl.createBuffer();
+ shape._webgl.buffers[3] = buffer;
+
+ gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
+ gl.bufferData(gl.ARRAY_BUFFER,
+ x3dom.Utils.getArrayBufferView(geoNode._vf.texCoordType, texcBuf),
+ gl.STATIC_DRAW);
+
+ gl.vertexAttribPointer(sp.texcoord, geoNode._mesh._numTexComponents,
+ shape._webgl.texCoordType, false,
+ shape._texCoordStrideOffset[0], shape._texCoordStrideOffset[1]);
+ gl.enableVertexAttribArray(sp.texcoord);
+ }
+
+ // colors
+ if (this.color)
+ {
+ buffer = gl.createBuffer();
+ shape._webgl.buffers[4] = buffer;
+
+ gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
+ gl.bufferData(gl.ARRAY_BUFFER,
+ x3dom.Utils.getArrayBufferView(geoNode._vf.colorType, colBuf),
+ gl.STATIC_DRAW);
+
+ gl.vertexAttribPointer(sp.color, geoNode._mesh._numColComponents,
+ shape._webgl.colorType, false,
+ shape._colorStrideOffset[0], shape._colorStrideOffset[1]);
+ gl.enableVertexAttribArray(sp.color);
+ }
+
+ // adjust sizes
+ geoNode._vf.vertexCount = [];
+ geoNode._vf.vertexCount[0] = posBuf.length / dataLen;
+
+ geoNode._mesh._numCoords = geoNode._vf.vertexCount[0];
+ geoNode._mesh._numFaces = geoNode._vf.vertexCount[0] / 3;
+
+ shape._webgl.primType = [];
+ shape._webgl.primType[0] = gl.TRIANGLES;
+
+ // cleanup
+ posBuf = null;
+ normBuf = null;
+ texcBuf = null;
+ colBuf = null;
+
+ this.index = null;
+ this.coord = null;
+ this.normal = null;
+ this.texCoord = null;
+ this.color = null;
+
+ that.checkError(gl);
+
+ // recreate shader
+ delete shape._webgl.shader;
+ shape._webgl.shader = currContext.cache.getDynamicShader(gl, viewarea, shape);
+ }
+ };
+
+ // index
+ if (binGeo._vf.index.length > 0)
+ {
+ var xmlhttp0 = new XMLHttpRequest();
+ xmlhttp0.open("GET", shape._nameSpace.getURL(binGeo._vf.index), true);
+ xmlhttp0.responseType = "arraybuffer";
+
+ shape._nameSpace.doc.downloadCount += 1;
+
+ xmlhttp0.send(null);
+
+ xmlhttp0.onload = function()
+ {
+ if (!shape._webgl)
+ return;
+
+ var XHR_buffer = xmlhttp0.response;
+
+ var geoNode = binGeo;
+ var attribTypeStr = geoNode._vf.indexType; //"Uint16"
+
+ var indexArray = x3dom.Utils.getArrayBufferView(attribTypeStr, XHR_buffer);
+
+ if (createTriangleSoup) {
+ shape._webgl.makeSeparateTris.pushBuffer("index", indexArray);
+ return;
+ }
+
+ var indicesBuffer = gl.createBuffer();
+ shape._webgl.buffers[0] = indicesBuffer;
+
+ if (x3dom.caps.INDEX_UINT && attribTypeStr == "Uint32") {
+ //indexArray is Uint32Array
+ shape._webgl.indexType = gl.UNSIGNED_INT;
+ }
+ else {
+ //indexArray is Uint16Array
+ shape._webgl.indexType = gl.UNSIGNED_SHORT;
+ }
+
+ gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indicesBuffer);
+ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indexArray, gl.STATIC_DRAW);
+
+ // Test reading Data
+ //x3dom.debug.logWarning("arraybuffer[0]="+indexArray[0]+"; n="+indexArray.length);
+
+ shape._webgl.binaryGeometry = 1; // indexed BG
+
+ if (geoNode._vf.vertexCount[0] == 0)
+ geoNode._vf.vertexCount[0] = indexArray.length;
+
+ geoNode._mesh._numFaces = 0;
+
+ for (var i=0; i<geoNode._vf.vertexCount.length; i++) {
+ if (shape._webgl.primType[i] == gl.TRIANGLE_STRIP)
+ geoNode._mesh._numFaces += geoNode._vf.vertexCount[i] - 2;
+ else
+ geoNode._mesh._numFaces += geoNode._vf.vertexCount[i] / 3;
+ }
+
+ indexArray = null;
+
+ shape._nameSpace.doc.downloadCount -= 1;
+ shape._webgl.internalDownloadCount -= 1;
+ if (shape._webgl.internalDownloadCount == 0)
+ shape._nameSpace.doc.needRender = true;
+
+ that.checkError(gl);
+
+ var t11 = new Date().getTime() - t00;
+ x3dom.debug.logInfo("XHR0/ index load time: " + t11 + " ms");
+ };
+ }
+
+ // interleaved array -- assume all attributes are given in one single array buffer
+ if (binGeo._hasStrideOffset && binGeo._vf.coord.length > 0)
+ {
+ var xmlhttp = new XMLHttpRequest();
+ xmlhttp.open("GET", shape._nameSpace.getURL(binGeo._vf.coord), true);
+ xmlhttp.responseType = "arraybuffer";
+
+ shape._nameSpace.doc.downloadCount += 1;
+
+ xmlhttp.send(null);
+
+ xmlhttp.onload = function()
+ {
+ if (!shape._webgl)
+ return;
+
+ var XHR_buffer = xmlhttp.response;
+
+ var geoNode = binGeo;
+ var attribTypeStr = geoNode._vf.coordType;
+
+ // assume same data type for all attributes (but might be wrong)
+ shape._webgl.coordType = x3dom.Utils.getVertexAttribType(attribTypeStr, gl);
+ shape._webgl.normalType = shape._webgl.coordType;
+ shape._webgl.texCoordType = shape._webgl.coordType;
+ shape._webgl.colorType = shape._webgl.coordType;
+
+ var attributes = x3dom.Utils.getArrayBufferView(attribTypeStr, XHR_buffer);
+
+ // calculate number of single data packages by including stride and type size
+ var dataLen = shape._coordStrideOffset[0] / x3dom.Utils.getDataTypeSize(attribTypeStr);
+ if (dataLen)
+ geoNode._mesh._numCoords = attributes.length / dataLen;
+
+ if (geoNode._vf.index.length == 0) {
+ for (var i=0; i<geoNode._vf.vertexCount.length; i++) {
+ if (shape._webgl.primType[i] == gl.TRIANGLE_STRIP)
+ geoNode._mesh._numFaces += geoNode._vf.vertexCount[i] - 2;
+ else
+ geoNode._mesh._numFaces += geoNode._vf.vertexCount[i] / 3;
+ }
+ }
+
+ var buffer = gl.createBuffer();
+
+ shape._webgl.buffers[1] = buffer;
+
+ gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
+ gl.bufferData(gl.ARRAY_BUFFER, attributes, gl.STATIC_DRAW);
+
+ gl.vertexAttribPointer(sp.position, geoNode._mesh._numPosComponents,
+ shape._webgl.coordType, false,
+ shape._coordStrideOffset[0], shape._coordStrideOffset[1]);
+ gl.enableVertexAttribArray(sp.position);
+
+ if (geoNode._vf.normal.length > 0)
+ {
+ shape._webgl.buffers[2] = buffer;
+
+ gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
+ gl.bufferData(gl.ARRAY_BUFFER, attributes, gl.STATIC_DRAW);
+
+ gl.vertexAttribPointer(sp.normal, geoNode._mesh._numNormComponents,
+ shape._webgl.normalType, false,
+ shape._normalStrideOffset[0], shape._normalStrideOffset[1]);
+ gl.enableVertexAttribArray(sp.normal);
+ }
+
+ if (geoNode._vf.texCoord.length > 0)
+ {
+ console.log("YUPPIE");
+
+ shape._webgl.buffers[3] = buffer;
+
+ gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
+ gl.bufferData(gl.ARRAY_BUFFER, attributes, gl.STATIC_DRAW);
+
+ gl.vertexAttribPointer(sp.texcoord, geoNode._mesh._numTexComponents,
+ shape._webgl.texCoordType, false,
+ shape._texCoordStrideOffset[0], shape._texCoordStrideOffset[1]);
+ gl.enableVertexAttribArray(sp.texcoord);
+ }
+
+ if (geoNode._vf.color.length > 0)
+ {
+ shape._webgl.buffers[4] = buffer;
+
+ gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
+ gl.bufferData(gl.ARRAY_BUFFER, attributes, gl.STATIC_DRAW);
+
+ gl.vertexAttribPointer(sp.color, geoNode._mesh._numColComponents,
+ shape._webgl.colorType, false,
+ shape._colorStrideOffset[0], shape._colorStrideOffset[1]);
+ gl.enableVertexAttribArray(sp.color);
+ }
+
+ attributes = null; // delete data block in CPU memory
+
+ shape._nameSpace.doc.downloadCount -= 1;
+ shape._webgl.internalDownloadCount -= 1;
+ if (shape._webgl.internalDownloadCount == 0)
+ shape._nameSpace.doc.needRender = true;
+
+ that.checkError(gl);
+
+ var t11 = new Date().getTime() - t00;
+ x3dom.debug.logInfo("XHR/ interleaved array load time: " + t11 + " ms");
+ };
+ }
+
+ // coord
+ if (!binGeo._hasStrideOffset && binGeo._vf.coord.length > 0)
+ {
+ var xmlhttp1 = new XMLHttpRequest();
+ xmlhttp1.open("GET", shape._nameSpace.getURL(binGeo._vf.coord), true);
+ xmlhttp1.responseType = "arraybuffer";
+
+ shape._nameSpace.doc.downloadCount += 1;
+
+ xmlhttp1.send(null);
+
+ xmlhttp1.onload = function()
+ {
+ if (!shape._webgl)
+ return;
+
+ var XHR_buffer = xmlhttp1.response;
+
+ var geoNode = binGeo;
+ var i = 0;
+
+ var attribTypeStr = geoNode._vf.coordType;
+ shape._webgl.coordType = x3dom.Utils.getVertexAttribType(attribTypeStr, gl);
+
+ var vertices = x3dom.Utils.getArrayBufferView(attribTypeStr, XHR_buffer);
+
+ if (createTriangleSoup) {
+ shape._webgl.makeSeparateTris.pushBuffer("coord", vertices);
+ return;
+ }
+
+ gl.bindAttribLocation(sp.program, 0, "position");
+
+ var positionBuffer = gl.createBuffer();
+ shape._webgl.buffers[1] = positionBuffer;
+ gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
+
+ gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);
+ gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
+
+ gl.vertexAttribPointer(sp.position,
+ geoNode._mesh._numPosComponents,
+ shape._webgl.coordType, false,
+ shape._coordStrideOffset[0], shape._coordStrideOffset[1]);
+ gl.enableVertexAttribArray(sp.position);
+
+ geoNode._mesh._numCoords = vertices.length / geoNode._mesh._numPosComponents;
+
+ if (geoNode._vf.index.length == 0) {
+ for (i=0; i<geoNode._vf.vertexCount.length; i++) {
+ if (shape._webgl.primType[i] == gl.TRIANGLE_STRIP)
+ geoNode._mesh._numFaces += geoNode._vf.vertexCount[i] - 2;
+ else
+ geoNode._mesh._numFaces += geoNode._vf.vertexCount[i] / 3;
+ }
+ }
+
+ // Test reading Data
+ //x3dom.debug.logWarning("arraybuffer[0].vx="+vertices[0]);
+
+ if ((attribTypeStr == "Float32") &&
+ (shape._vf.bboxSize.x < 0 || shape._vf.bboxSize.y < 0 || shape._vf.bboxSize.z < 0))
+ {
+ var min = new x3dom.fields.SFVec3f(vertices[0],vertices[1],vertices[2]);
+ var max = new x3dom.fields.SFVec3f(vertices[0],vertices[1],vertices[2]);
+
+ for (i=3; i<vertices.length; i+=3)
+ {
+ if (min.x > vertices[i+0]) { min.x = vertices[i+0]; }
+ if (min.y > vertices[i+1]) { min.y = vertices[i+1]; }
+ if (min.z > vertices[i+2]) { min.z = vertices[i+2]; }
+
+ if (max.x < vertices[i+0]) { max.x = vertices[i+0]; }
+ if (max.y < vertices[i+1]) { max.y = vertices[i+1]; }
+ if (max.z < vertices[i+2]) { max.z = vertices[i+2]; }
+ }
+
+ // TODO; move to mesh for all cases?
+ shape._vf.bboxCenter.setValues(min.add(max).multiply(0.5));
+ shape._vf.bboxSize.setValues(max.subtract(min));
+ }
+
+ vertices = null;
+
+ shape._nameSpace.doc.downloadCount -= 1;
+ shape._webgl.internalDownloadCount -= 1;
+ if (shape._webgl.internalDownloadCount == 0)
+ shape._nameSpace.doc.needRender = true;
+
+ that.checkError(gl);
+
+ var t11 = new Date().getTime() - t00;
+ x3dom.debug.logInfo("XHR1/ coord load time: " + t11 + " ms");
+ };
+ }
+
+ // normal
+ if (!binGeo._hasStrideOffset && binGeo._vf.normal.length > 0)
+ {
+ var xmlhttp2 = new XMLHttpRequest();
+ xmlhttp2.open("GET", shape._nameSpace.getURL(binGeo._vf.normal), true);
+ xmlhttp2.responseType = "arraybuffer";
+
+ shape._nameSpace.doc.downloadCount += 1;
+
+ xmlhttp2.send(null);
+
+ xmlhttp2.onload = function()
+ {
+ if (!shape._webgl)
+ return;
+
+ var XHR_buffer = xmlhttp2.response;
+
+ var attribTypeStr = binGeo._vf.normalType;
+ shape._webgl.normalType = x3dom.Utils.getVertexAttribType(attribTypeStr, gl);
+
+ var normals = x3dom.Utils.getArrayBufferView(attribTypeStr, XHR_buffer);
+
+ if (createTriangleSoup) {
+ shape._webgl.makeSeparateTris.pushBuffer("normal", normals);
+ return;
+ }
+
+ var normalBuffer = gl.createBuffer();
+ shape._webgl.buffers[2] = normalBuffer;
+
+ gl.bindBuffer(gl.ARRAY_BUFFER, normalBuffer);
+ gl.bufferData(gl.ARRAY_BUFFER, normals, gl.STATIC_DRAW);
+
+ gl.vertexAttribPointer(sp.normal,
+ binGeo._mesh._numNormComponents,
+ shape._webgl.normalType, false,
+ shape._normalStrideOffset[0], shape._normalStrideOffset[1]);
+ gl.enableVertexAttribArray(sp.normal);
+
+ // Test reading Data
+ //x3dom.debug.logWarning("arraybuffer[0].nx="+normals[0]);
+
+ normals = null;
+
+ shape._nameSpace.doc.downloadCount -= 1;
+ shape._webgl.internalDownloadCount -= 1;
+ if (shape._webgl.internalDownloadCount == 0)
+ shape._nameSpace.doc.needRender = true;
+
+ that.checkError(gl);
+
+ var t11 = new Date().getTime() - t00;
+ x3dom.debug.logInfo("XHR2/ normal load time: " + t11 + " ms");
+ };
+ }
+
+ // texCoord
+ if (!binGeo._hasStrideOffset && binGeo._vf.texCoord.length > 0)
+ {
+ var xmlhttp3 = new XMLHttpRequest();
+ xmlhttp3.open("GET", shape._nameSpace.getURL(binGeo._vf.texCoord), true);
+ xmlhttp3.responseType = "arraybuffer";
+
+ shape._nameSpace.doc.downloadCount += 1;
+
+ xmlhttp3.send(null);
+
+ xmlhttp3.onload = function()
+ {
+ var i, j;
+ var tmp;
+
+ if (!shape._webgl)
+ return;
+
+ var XHR_buffer = xmlhttp3.response;
+
+ var attribTypeStr = binGeo._vf.texCoordType;
+ shape._webgl.texCoordType = x3dom.Utils.getVertexAttribType(attribTypeStr, gl);
+
+ var texCoords = x3dom.Utils.getArrayBufferView(attribTypeStr, XHR_buffer);
+
+ if (createTriangleSoup) {
+ shape._webgl.makeSeparateTris.pushBuffer("texCoord", texCoords);
+ return;
+ }
+
+
+ //if IDs are given in texture coordinates, interpret texcoords as ID buffer
+ if (binGeo._vf["idsPerVertex"])
+ {
+ var idBuffer = gl.createBuffer();
+
+ shape._webgl.buffers[5] = idBuffer;
+
+ gl.bindBuffer(gl.ARRAY_BUFFER, idBuffer);
+
+ //Create a buffer for the ids with half size of the texccoord buffer
+ var ids = x3dom.Utils.getArrayBufferView("Float32", texCoords.length/2);
+
+ //swap x and y, in order to interpret tex coords as FLOAT later on
+ for (i = 0, j= 0; i < texCoords.length; i+=2, j++)
+ {
+ ids[j] = texCoords[i+1] * 65536 + texCoords[i];
+ }
+
+ gl.bufferData(gl.ARRAY_BUFFER, ids, gl.STATIC_DRAW);
+
+ gl.vertexAttribPointer(sp.id,
+ 1,
+ gl.FLOAT, false,
+ 4, 0);
+ gl.enableVertexAttribArray(sp.id);
+ }
+ else
+ {
+ var texcBuffer = gl.createBuffer();
+ shape._webgl.buffers[3] = texcBuffer;
+
+ gl.bindBuffer(gl.ARRAY_BUFFER, texcBuffer);
+ gl.bufferData(gl.ARRAY_BUFFER, texCoords, gl.STATIC_DRAW);
+
+ gl.vertexAttribPointer(sp.texcoord,
+ binGeo._mesh._numTexComponents,
+ shape._webgl.texCoordType, false,
+ shape._texCoordStrideOffset[0], shape._texCoordStrideOffset[1]);
+ gl.enableVertexAttribArray(sp.texcoord);
+ }
+ // Test reading Data
+ //x3dom.debug.logWarning("arraybuffer[0].tx="+texCoords[0]);
+
+ texCoords = null;
+
+ shape._nameSpace.doc.downloadCount -= 1;
+ shape._webgl.internalDownloadCount -= 1;
+ if (shape._webgl.internalDownloadCount == 0)
+ shape._nameSpace.doc.needRender = true;
+
+ that.checkError(gl);
+
+ var t11 = new Date().getTime() - t00;
+ x3dom.debug.logInfo("XHR3/ texCoord load time: " + t11 + " ms");
+ };
+ }
+
+ // color
+ if (!binGeo._hasStrideOffset && binGeo._vf.color.length > 0)
+ {
+ var xmlhttp4 = new XMLHttpRequest();
+ xmlhttp4.open("GET", shape._nameSpace.getURL(binGeo._vf.color), true);
+ xmlhttp4.responseType = "arraybuffer";
+
+ shape._nameSpace.doc.downloadCount += 1;
+
+ xmlhttp4.send(null);
+
+ xmlhttp4.onload = function()
+ {
+ if (!shape._webgl)
+ return;
+
+ var XHR_buffer = xmlhttp4.response;
+
+ var attribTypeStr = binGeo._vf.colorType;
+ shape._webgl.colorType = x3dom.Utils.getVertexAttribType(attribTypeStr, gl);
+
+ var colors = x3dom.Utils.getArrayBufferView(attribTypeStr, XHR_buffer);
+
+ if (createTriangleSoup) {
+ shape._webgl.makeSeparateTris.pushBuffer("color", colors);
+ return;
+ }
+
+ var colorBuffer = gl.createBuffer();
+ shape._webgl.buffers[4] = colorBuffer;
+
+ gl.bindBuffer(gl.ARRAY_BUFFER, colorBuffer);
+ gl.bufferData(gl.ARRAY_BUFFER, colors, gl.STATIC_DRAW);
+
+ gl.vertexAttribPointer(sp.color,
+ binGeo._mesh._numColComponents,
+ shape._webgl.colorType, false,
+ shape._colorStrideOffset[0], shape._colorStrideOffset[1]);
+ gl.enableVertexAttribArray(sp.color);
+
+ // Test reading Data
+ //x3dom.debug.logWarning("arraybuffer[0].cx="+colors[0]);
+
+ colors = null;
+
+ shape._nameSpace.doc.downloadCount -= 1;
+ shape._webgl.internalDownloadCount -= 1;
+ if (shape._webgl.internalDownloadCount == 0)
+ shape._nameSpace.doc.needRender = true;
+
+ that.checkError(gl);
+
+ var t11 = new Date().getTime() - t00;
+ x3dom.debug.logInfo("XHR4/ color load time: " + t11 + " ms");
+ };
+ }
+ // TODO: tangent AND binormal
+};
+
+/** setup/download pop geometry */
+x3dom.BinaryContainerLoader.setupPopGeo = function(shape, sp, gl, viewarea, currContext)
+{
+ if (this.outOfMemory) {
+ return;
+ }
+
+ var popGeo = shape._cf.geometry.node;
+
+ //reserve space for vertex buffer (and index buffer if any) on the gpu
+ if (popGeo.hasIndex()) {
+ shape._webgl.popGeometry = 1;
+
+ shape._webgl.buffers[0] = gl.createBuffer();
+
+ gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, shape._webgl.buffers[0]);
+ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, popGeo.getTotalNumberOfIndices()*2, gl.STATIC_DRAW);
+
+ //this is a workaround to mimic gl_VertexID
+ shape._webgl.buffers[5] = gl.createBuffer();
+
+ var idBuffer = new Float32Array(popGeo._vf.vertexBufferSize);
+
+ (function(){ for (var i = 0; i < idBuffer.length; ++i) idBuffer[i] = i; })();
+
+ gl.bindBuffer(gl.ARRAY_BUFFER, shape._webgl.buffers[5]);
+ gl.bufferData(gl.ARRAY_BUFFER, idBuffer, gl.STATIC_DRAW);
+ }
+ else {
+ shape._webgl.popGeometry = -1;
+ }
+
+ shape._webgl.buffers[1] = gl.createBuffer();
+
+ gl.bindBuffer(gl.ARRAY_BUFFER, shape._webgl.buffers[1]);
+ gl.bufferData(gl.ARRAY_BUFFER, (popGeo._vf.attributeStride * popGeo._vf.vertexBufferSize), gl.STATIC_DRAW);
+
+
+ //setup general render settings
+ var attribTypeStr = popGeo._vf.coordType;
+ shape._webgl.coordType = x3dom.Utils.getVertexAttribType(attribTypeStr, gl);
+
+ shape._coordStrideOffset[0] = popGeo.getAttributeStride();
+ shape._coordStrideOffset[1] = popGeo.getPositionOffset();
+
+ gl.vertexAttribPointer(sp.position, shape._cf.geometry.node._mesh._numPosComponents, shape._webgl.coordType,
+ false, shape._coordStrideOffset[0], shape._coordStrideOffset[1]);
+ gl.enableVertexAttribArray(sp.position);
+
+ if (popGeo.hasNormal()) {
+ attribTypeStr = popGeo._vf.normalType;
+ shape._webgl.normalType = x3dom.Utils.getVertexAttribType(attribTypeStr, gl);
+
+ shape._normalStrideOffset[0] = popGeo.getAttributeStride();
+ shape._normalStrideOffset[1] = popGeo.getNormalOffset();
+
+ shape._webgl.buffers[2] = shape._webgl.buffers[1]; //use interleaved vertex data buffer
+
+ gl.vertexAttribPointer(sp.normal, shape._cf.geometry.node._mesh._numNormComponents, shape._webgl.normalType,
+ false, shape._normalStrideOffset[0], shape._normalStrideOffset[1]);
+ gl.enableVertexAttribArray(sp.normal);
+ }
+ if (popGeo.hasTexCoord()) {
+ attribTypeStr = popGeo._vf.texCoordType;
+ shape._webgl.texCoordType = x3dom.Utils.getVertexAttribType(attribTypeStr, gl);
+
+ shape._webgl.buffers[3] = shape._webgl.buffers[1]; //use interleaved vertex data buffer
+
+ shape._texCoordStrideOffset[0] = popGeo.getAttributeStride();
+ shape._texCoordStrideOffset[1] = popGeo.getTexCoordOffset();
+
+ gl.vertexAttribPointer(sp.texcoord, shape._cf.geometry.node._mesh._numTexComponents, shape._webgl.texCoordType,
+ false, shape._texCoordStrideOffset[0], shape._texCoordStrideOffset[1]);
+ gl.enableVertexAttribArray(sp.texcoord);
+ }
+ if (popGeo.hasColor()) {
+ attribTypeStr = popGeo._vf.colorType;
+ shape._webgl.colorType = x3dom.Utils.getVertexAttribType(attribTypeStr, gl);
+
+ shape._webgl.buffers[4] = shape._webgl.buffers[1]; //use interleaved vertex data buffer
+
+ shape._colorStrideOffset[0] = popGeo.getAttributeStride();
+ shape._colorStrideOffset[1] = popGeo.getColorOffset();
+
+ gl.vertexAttribPointer(sp.color, shape._cf.geometry.node._mesh._numColComponents, shape._webgl.colorType,
+ false, shape._colorStrideOffset[0], shape._colorStrideOffset[1]);
+ gl.enableVertexAttribArray(sp.color);
+ }
+
+ shape._webgl.currentNumIndices = 0;
+ shape._webgl.currentNumVertices = 0;
+ shape._webgl.numVerticesAtLevel = [];
+ shape._webgl.levelsAvailable = 0;
+
+ this.checkError(gl);
+
+ shape._webgl.levelLoaded = [];
+ (function() {
+ for (var i = 0; i < popGeo.getNumLevels(); ++i)
+ shape._webgl.levelLoaded.push(false);
+ })();
+
+ //download callback, used to simply upload received vertex data to the GPU
+ var uploadDataToGPU = function(data, lvl) {
+ //x3dom.debug.logInfo("PopGeometry: Received data for level " + lvl + " !\n");
+
+ shape._webgl.levelLoaded[lvl] = true;
+ shape._webgl.numVerticesAtLevel[lvl] = 0;
+
+ if (data) {
+ //perform gpu data upload
+ var indexDataLengthInBytes = 0;
+ var redrawNeeded = false;
+
+ if (popGeo.hasIndex()) {
+ indexDataLengthInBytes = popGeo.getNumIndicesByLevel(lvl)*2;
+
+ if (indexDataLengthInBytes > 0) {
+ redrawNeeded = true;
+
+ var indexDataView = new Uint8Array(data, 0, indexDataLengthInBytes);
+
+ gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, shape._webgl.buffers[0]);
+ //index data is always placed where it belongs, as we have to keep the order of rendering
+ (function() {
+ var indexDataOffset = 0;
+
+ for (var i = 0; i < lvl; ++i) { indexDataOffset += popGeo.getNumIndicesByLevel(i); }
+
+ gl.bufferSubData(gl.ELEMENT_ARRAY_BUFFER, indexDataOffset*2, indexDataView);
+ })();
+ }
+ }
+
+ var vertexDataLengthInBytes = data.byteLength - indexDataLengthInBytes;
+
+ if (vertexDataLengthInBytes > 0) {
+ redrawNeeded = true;
+
+ var attributeDataView = new Uint8Array(data, indexDataLengthInBytes, vertexDataLengthInBytes);
+
+ gl.bindBuffer(gl.ARRAY_BUFFER, shape._webgl.buffers[1]);
+ if (!popGeo.hasIndex()) {
+ //on non-indexed rendering, vertex data is just appended, the order of vertex data packages doesn't matter
+ gl.bufferSubData(gl.ARRAY_BUFFER, shape._webgl.currentNumVertices * popGeo.getAttributeStride(),
+ attributeDataView);
+ }
+ else {
+ //on indexed rendering, vertex data is always placed where it belongs, as we have to keep the indexed order
+ gl.bufferSubData(gl.ARRAY_BUFFER,popGeo.getVertexDataBufferOffset(lvl) * popGeo.getAttributeStride(),
+ attributeDataView);
+ }
+
+ //adjust render settings: vertex data
+ shape._webgl.numVerticesAtLevel[lvl] = vertexDataLengthInBytes / popGeo.getAttributeStride();
+ shape._webgl.currentNumVertices += shape._webgl.numVerticesAtLevel[lvl];
+ }
+
+ //compute number of valid indices
+ (function() {
+ var numValidIndices = 0;
+
+ for (var i = shape._webgl.levelsAvailable; i < popGeo.getNumLevels(); ++i) {
+ if (shape._webgl.levelLoaded[i] === false) {
+ break;
+ }
+ else {
+ numValidIndices += popGeo.getNumIndicesByLevel(i);
+ ++shape._webgl.levelsAvailable;
+ }
+ }
+
+ //adjust render settings: index data
+ shape._webgl.currentNumIndices = numValidIndices;
+ })();
+
+ //here, we tell X3DOM how many faces / vertices get displayed in the stats
+ popGeo._mesh._numCoords = shape._webgl.currentNumVertices;
+ //@todo: this assumes pure TRIANGLES data
+ popGeo._mesh._numFaces = (popGeo.hasIndex() ? shape._webgl.currentNumIndices : shape._webgl.currentNumVertices) / 3;
+
+ //here, we tell X3DOM how many vertices get rendered
+ //@todo: this assumes pure TRIANGLES data
+ popGeo.adaptVertexCount(popGeo.hasIndex() ? popGeo._mesh._numFaces * 3 : popGeo._mesh._numCoords);
+ //x3dom.debug.logInfo("PopGeometry: Loaded level " + lvl + " data to gpu, model has now " +
+ // popGeo._mesh._numCoords + " vertices and " + popGeo._mesh._numFaces + " triangles, " +
+ // (new Date().getTime() - shape._webgl.downloadStartTimer) + " ms after posting download requests");
+
+ //request redraw, if necessary
+ if (redrawNeeded) {
+ shape._nameSpace.doc.needRender = true;
+ }
+ }
+ };
+
+ //post XHRs
+ var dataURLs = popGeo.getDataURLs();
+
+ var downloadCallbacks = [];
+ var priorities = [];
+
+ shape._webgl.downloadStartTimer = new Date().getTime();
+
+ //CODE WITH DL MANAGER
+ //use the DownloadManager to prioritize loading
+
+ for (var i = 0; i < dataURLs.length; ++i) {
+ shape._nameSpace.doc.downloadCount += 1;
+
+ (function(idx) {
+ downloadCallbacks.push(function(data) {
+ shape._nameSpace.doc.downloadCount -= 1;
+ return uploadDataToGPU(data, idx);
+ });
+ })(i);
+
+ priorities.push(i);
+ }
+
+ x3dom.DownloadManager.get(dataURLs, downloadCallbacks, priorities);
+ //END CODE WITH DL MANAGER
+};
+
+/** setup/download image geometry */
+x3dom.BinaryContainerLoader.setupImgGeo = function(shape, sp, gl, viewarea, currContext)
+{
+ if (this.outOfMemory) {
+ return;
+ }
+
+ var imageGeometry = shape._cf.geometry.node;
+
+ if ( imageGeometry.getIndexTexture() ) {
+ shape._webgl.imageGeometry = 1;
+ } else {
+ shape._webgl.imageGeometry = -1;
+ }
+
+ imageGeometry.unsetGeoDirty();
+
+ if (currContext.IG_PositionBuffer == null) {
+ currContext.IG_PositionBuffer = gl.createBuffer();
+ }
+
+ shape._webgl.buffers[1] = currContext.IG_PositionBuffer;
+ gl.bindBuffer(gl.ARRAY_BUFFER, currContext.IG_PositionBuffer);
+
+ var vertices = new Float32Array(shape._webgl.positions[0]);
+
+ gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);
+ gl.bindBuffer(gl.ARRAY_BUFFER, currContext.IG_PositionBuffer);
+
+ gl.vertexAttribPointer(sp.position, imageGeometry._mesh._numPosComponents,
+ shape._webgl.coordType, false,
+ shape._coordStrideOffset[0], shape._coordStrideOffset[1]);
+ gl.enableVertexAttribArray(sp.position);
+
+ vertices = null;
+
+ this.checkError(gl);
+};
+
+/*
+ * X3DOM JavaScript Library
+ * http://www.x3dom.org
+ *
+ * (C)2009 Fraunhofer IGD, Darmstadt, Germany
+ * Dual licensed under the MIT and GPL
+ *
+ * Based on code originally provided by
+ * Philip Taylor: http://philip.html5.org
+ */
+
+
+/**
+ * c'tor
+ */
+x3dom.DrawableCollection = function (drawableCollectionConfig) {
+ this.collection = [];
+
+ this.viewMatrix = drawableCollectionConfig.viewMatrix;
+ this.projMatrix = drawableCollectionConfig.projMatrix;
+ this.sceneMatrix = drawableCollectionConfig.sceneMatrix;
+
+ this.viewarea = drawableCollectionConfig.viewArea;
+
+ var scene = this.viewarea._scene;
+ var env = scene.getEnvironment();
+ var viewpoint = scene.getViewpoint();
+
+ this.near = viewpoint.getNear();
+ this.pixelHeightAtDistOne = viewpoint.getImgPlaneHeightAtDistOne() / this.viewarea._height;
+
+ this.context = drawableCollectionConfig.context;
+ this.gl = drawableCollectionConfig.gl;
+
+ this.viewFrustum = this.viewarea.getViewfrustum(this.sceneMatrix);
+ this.worldVol = new x3dom.fields.BoxVolume(); // helper
+
+ this.frustumCulling = drawableCollectionConfig.frustumCulling && (this.viewFrustum != null);
+ this.smallFeatureThreshold = drawableCollectionConfig.smallFeatureThreshold;
+
+ // if (lowPriorityThreshold < 1) sort all potentially visible objects according to priority
+ this.sortOpaque = (this.smallFeatureThreshold > 0 && env._lowPriorityThreshold < 1);
+ this.sortTrans = drawableCollectionConfig.sortTrans;
+
+ this.prioLevels = 10;
+ this.maxTreshold = 100;
+
+ this.sortBySortKey = false;
+ this.sortByPriority = false;
+
+ this.numberOfNodes = 0;
+
+ this.length = 0;
+};
+
+/**
+ * graphState = {
+ * boundedNode: backref to bounded node object
+ * localMatrix: mostly identity
+ * globalMatrix: current transform
+ * volume: local bbox
+ * worldVolume: global bbox
+ * center: center in eye coords
+ * coverage: currently approx. number of pixels on screen
+ * };
+ */
+x3dom.DrawableCollection.prototype.cull = function (transform, graphState, singlePath, planeMask) {
+ var node = graphState.boundedNode; // get ref to SG node
+
+ if (!node || !node._vf.render) {
+ return 0; // <0 outside, >0 inside, but can't tell in this case
+ }
+
+ var volume = node.getVolume(); // create on request
+ var MASK_SET = 63; // 2^6-1, i.e. all sides of the volume
+
+ if (this.frustumCulling && graphState.needCulling) {
+ var wvol;
+
+ if (singlePath && !graphState.worldVolume.isValid()) {
+ graphState.worldVolume.transformFrom(transform, volume);
+ wvol = graphState.worldVolume; // use opportunity to update if necessary
+ }
+ else if (planeMask < MASK_SET) {
+ this.worldVol.transformFrom(transform, volume);
+ wvol = this.worldVol;
+ }
+
+ if (planeMask < MASK_SET)
+ planeMask = this.viewFrustum.intersect(wvol, planeMask);
+ if (planeMask <= 0) {
+ return -1; // if culled return -1; 0 should never happen
+ }
+ }
+ else {
+ planeMask = MASK_SET;
+ }
+
+ graphState.coverage = -1; // if -1 then ignore value later on
+
+ // TODO: save the coverage only for drawables, which are unique (shapes can be shared!)
+ if (this.smallFeatureThreshold > 0 || node.forceUpdateCoverage()) {
+ var modelViewMat = this.viewMatrix.mult(transform);
+
+ graphState.center = modelViewMat.multMatrixPnt(volume.getCenter());
+
+ var rVec = modelViewMat.multMatrixVec(volume.getRadialVec());
+ var r = rVec.length();
+
+ var dist = Math.max(-graphState.center.z - r, this.near);
+ var projPixelLength = dist * this.pixelHeightAtDistOne;
+
+ graphState.coverage = (r * 2.0) / projPixelLength;
+
+ if (this.smallFeatureThreshold > 0 && graphState.coverage < this.smallFeatureThreshold &&
+ graphState.needCulling) {
+ return 0; // differentiate between outside and this case
+ }
+ }
+
+ // not culled, incr node cnt
+ this.numberOfNodes++;
+
+ return planeMask; // >0, inside
+};
+
+/**
+ * A drawable is basically a unique pair of a shape node and a global transformation.
+ */
+x3dom.DrawableCollection.prototype.addShape = function (shape, transform, graphState) {
+ //Create a new drawable object
+ var drawable = {};
+
+ //Set the shape
+ drawable.shape = shape;
+
+ //Set the transform
+ drawable.transform = transform;
+
+ drawable.localTransform = graphState.localMatrix;
+
+ //Set the local bounding box (reference, can be shared amongst shapes)
+ drawable.localVolume = graphState.volume;
+
+ //Set the global bbox (needs to be cloned since shape can be shared)
+ drawable.worldVolume = x3dom.fields.BoxVolume.copy(graphState.worldVolume);
+
+ //Calculate the magical object priority (though currently not very magic)
+ drawable.priority = Math.max(0, graphState.coverage);
+ //drawable.priority = this.calculatePriority(graphState);
+
+ //Get shaderID from shape
+ drawable.shaderID = shape.getShaderProperties(this.viewarea).id;
+
+ var appearance = shape._cf.appearance.node;
+
+ drawable.sortType = appearance ? appearance._vf.sortType.toLowerCase() : "opaque";
+ drawable.sortKey = appearance ? appearance._vf.sortKey : 0;
+
+ if (drawable.sortType == 'transparent') {
+ if (this.smallFeatureThreshold > 0) {
+ // TODO: center was previously set in cull, which is called first, but this
+ // might be problematic if scene is traversed in parallel and node is shared
+ // (though currently traversal is sequential, so everything is fine)
+ drawable.zPos = graphState.center.z;
+ }
+ else {
+ //Calculate the z-Pos for transparent object sorting
+ //if the center of the box is not available
+ var center = transform.multMatrixPnt(shape.getCenter());
+ center = this.viewMatrix.multMatrixPnt(center);
+ drawable.zPos = center.z;
+ }
+ }
+
+ //Look for sorting by sortKey
+ if (!this.sortBySortKey && drawable.sortKey != 0) {
+ this.sortBySortKey = true;
+ }
+
+ //Generate separate array for sortType if not exists
+ if (this.collection[drawable.sortType] === undefined) {
+ this.collection[drawable.sortType] = [];
+ }
+
+ //Push drawable to the collection
+ this.collection[drawable.sortType].push(drawable);
+ //this.collection[drawable.sortType][drawable.sortKey][drawable.priority][drawable.shaderID].push(drawable);
+
+ //Increment collection length
+ this.length++;
+
+ //Finally setup shape directly here to avoid another loop of O(n)
+ if (this.context && this.gl) {
+ this.context.setupShape(this.gl, drawable, this.viewarea);
+ }
+ //TODO: what about Flash? Shall we also setup structures here?
+};
+
+/**
+ * A drawable is basically a unique pair of a shape node and a global transformation.
+ */
+x3dom.DrawableCollection.prototype.addDrawable = function (drawable) {
+ //Calculate the magical object priority (though currently not very magic)
+ //drawable.priority = this.calculatePriority(graphState);
+
+ //Get shaderID from shape
+ drawable.shaderID = drawable.shape.getShaderProperties(this.viewarea).id;
+
+ var appearance = drawable.shape._cf.appearance.node;
+
+ drawable.sortType = appearance ? appearance._vf.sortType.toLowerCase() : "opaque";
+ drawable.sortKey = appearance ? appearance._vf.sortKey : 0;
+
+ if (drawable.sortType == 'transparent') {
+ //TODO set zPos for drawable for z-sorting
+ //Calculate the z-Pos for transparent object sorting
+ //if the center of the box is not available
+ var center = drawable.transform.multMatrixPnt(drawable.shape.getCenter());
+ center = this.viewMatrix.multMatrixPnt(center);
+ drawable.zPos = center.z;
+ }
+
+ //Look for sorting by sortKey
+ if (!this.sortBySortKey && drawable.sortKey != 0) {
+ this.sortBySortKey = true;
+ }
+
+ //Generate separate array for sortType if not exists
+ if (this.collection[drawable.sortType] === undefined) {
+ this.collection[drawable.sortType] = [];
+ }
+
+ //Push drawable to the collection
+ this.collection[drawable.sortType].push(drawable);
+ //this.collection[drawable.sortType][drawable.sortKey][drawable.priority][drawable.shaderID].push(drawable);
+
+ //Increment collection length
+ this.length++;
+
+ //Finally setup shape directly here to avoid another loop of O(n)
+ if (this.context && this.gl) {
+ this.context.setupShape(this.gl, drawable, this.viewarea);
+ }
+};
+
+
+/**
+ * Calculate the magical object priority (though currently not very magic).
+ */
+x3dom.DrawableCollection.prototype.calculatePriority = function (graphState) {
+ //Use coverage as priority
+ var priority = Math.max(0, graphState.coverage);
+
+ //Classify the priority level
+ var pl = this.prioLevels - 1; // Can this be <= 0? Then FIXME!
+ priority = Math.min( Math.round(priority / (this.maxTreshold / pl)), pl );
+
+ return priority;
+};
+
+/**
+ * Concatenate opaque and transparent drawables
+ */
+x3dom.DrawableCollection.prototype.concat = function () {
+ var opaque = (this.collection['opaque'] !== undefined) ? this.collection['opaque'] : [];
+ var transparent = (this.collection['transparent'] !== undefined) ? this.collection['transparent'] : [];
+
+ //Merge opaque and transparent drawables to a single array
+ this.collection = opaque.concat(transparent);
+};
+
+/**
+ * Get drawable for id
+ */
+x3dom.DrawableCollection.prototype.get = function (idx) {
+ return this.collection[idx];
+};
+
+/**
+ * Sort the DrawableCollection
+ */
+x3dom.DrawableCollection.prototype.sort = function () {
+ var opaque = [];
+ var transparent = [];
+ var that = this;
+
+ //Sort opaque drawables
+ if (this.collection['opaque'] !== undefined) {
+ // never call this for very big scenes, getting very slow; try binning approach
+ if (this.sortOpaque) {
+ this.collection['opaque'].sort(function (a, b) {
+ if (a.sortKey == b.sortKey || !that.sortBySortKey) {
+ //Second sort criteria (priority)
+ return b.priority - a.priority;
+ }
+ //First sort criteria (sortKey)
+ return a.sortKey - b.sortKey;
+ });
+ }
+ opaque = this.collection['opaque'];
+ }
+
+ //Sort transparent drawables
+ if (this.collection['transparent'] !== undefined) {
+ if (this.sortTrans) {
+ this.collection['transparent'].sort(function (a, b) {
+ if (a.sortKey == b.sortKey || !that.sortBySortKey) {
+ if (a.priority == b.priority || !that.sortByPriority) {
+ //Third sort criteria (zPos)
+ return a.zPos - b.zPos;
+ }
+ //Second sort criteria (priority)
+ return b.priority - a.priority;
+ }
+ //First sort criteria (sortKey)
+ return a.sortKey - b.sortKey;
+ });
+ }
+ transparent = this.collection['transparent'];
+ }
+
+ //Merge opaque and transparent drawables to a single array (slow operation)
+ this.collection = opaque.concat(transparent);
+};
+
+x3dom.DrawableCollection.prototype.forEach = function (fnc, maxPriority) {
+ //Set maximal priority
+ maxPriority = (maxPriority !== undefined) ? Math.min(maxPriority, this.prioLevels) : this.prioLevels;
+
+ //Define run variables
+ var sortKey, priority, shaderID, drawable;
+
+ //First traverse Opaque drawables
+ // TODO; FIXME; this is wrong, sortKey can also be negative!
+ for (sortKey=0; sortKey<this.collection['opaque'].length; ++sortKey)
+ {
+ if (this.collection['opaque'][sortKey] !== undefined)
+ {
+ for (priority=this.collection['opaque'][sortKey].length; priority>0; --priority)
+ {
+ if (this.collection['opaque'][sortKey][priority] !== undefined)
+ {
+ for (shaderID in this.collection['opaque'][sortKey][priority])
+ {
+ for (drawable=0; drawable<this.collection['opaque'][sortKey][priority][shaderID].length; ++drawable)
+ {
+ fnc( this.collection['opaque'][sortKey][priority][shaderID][drawable] );
+ }
+ }
+ }
+ }
+ }
+ }
+
+ //Next traverse transparent drawables
+ // TODO; FIXME; this is wrong, sortKey can also be negative!
+ for (sortKey=0; sortKey<this.collection['transparent'].length; ++sortKey)
+ {
+ if (this.collection['transparent'][sortKey] !== undefined)
+ {
+ for (priority=this.collection['transparent'][sortKey].length; priority>0; --priority)
+ {
+ if (this.collection['transparent'][sortKey][priority] !== undefined)
+ {
+ for (var shaderId in this.collection['transparent'][sortKey][priority])
+ {
+ //Sort transparent drawables by z-Pos
+ this.collection['transparent'][sortKey][priority][shaderId].sort(function(a, b) {
+ return a.zPos - b.zPos
+ });
+
+ for (drawable=0; drawable<this.collection['transparent'][sortKey][priority][shaderId].length; ++drawable)
+ {
+ fnc( this.collection['transparent'][sortKey][priority][shaderId][drawable] );
+ }
+ }
+ }
+ }
+ }
+ }
+};
+
+/*
+ * X3DOM JavaScript Library
+ * http://www.x3dom.org
+ *
+ * (C)2009 Fraunhofer IGD, Darmstadt, Germany
+ * Dual licensed under the MIT and GPL
+ *
+ * Based on code originally provided by
+ * Philip Taylor: http://philip.html5.org
+ */
+
+
+/**
+ * Moveable interface, wraps x3d bounded node with SpaceSensor-like movement functionality,
+ * therefore attaches event handlers, thus to be called earliest in document.onload method.
+ *
+ * Cleanup backrefs and listeners on delete by explicitly calling detachHandlers()
+ */
+x3dom.Moveable = function(x3domElem, boundedObj, callback, gridSize, mode) {
+ this._x3domRoot = x3domElem;
+ this._runtime = x3domElem.runtime;
+
+ // callback function for notifying changes
+ this._callback = callback;
+
+ // snap to grid of given size (0, no grid, if undefined)
+ this._gridSize = gridSize ? gridSize : 0;
+
+ this._moveable = boundedObj;
+ this._drag = false;
+
+ this._w = 0;
+ this._h = 0;
+
+ this._uPlane = null;
+ this._vPlane = null;
+ this._pPlane = null;
+
+ this._isect = null;
+
+ this._translationOffset = null;
+ this._rotationOffset = null;
+ this._scaleOffset = null;
+
+ this._lastX = 0;
+ this._lastY = 0;
+ this._buttonState = 0;
+
+ this._mode = (mode && mode.length) ? mode.toLowerCase() : "translation"; //"all";
+
+ this._firstRay = null;
+ this._matrixTrafo = null;
+
+ this._navType = "examine";
+
+ this.attachHandlers();
+};
+
+// grid size setter, for snapping
+x3dom.Moveable.prototype.setGridSize = function(gridSize) {
+ this._gridSize = gridSize;
+};
+
+// interaction mode setter, for translation and/or rotation
+x3dom.Moveable.prototype.setMode = function(mode) {
+ this._mode = mode.toLowerCase();
+};
+
+x3dom.Moveable.prototype.attachHandlers = function() {
+ // add backref to movable object (for member access and wrapping)
+ this._moveable._iMove = this;
+
+ // add backref to <x3d> element
+ if (!this._x3domRoot._iMove)
+ this._x3domRoot._iMove = [];
+ this._x3domRoot._iMove.push(this);
+
+ // mouse events
+ this._moveable.addEventListener('mousedown', this.start, false);
+ this._moveable.addEventListener('mouseover', this.over, false);
+ this._moveable.addEventListener('mouseout', this.out, false);
+
+ if (this._x3domRoot._iMove.length == 1) {
+ // more mouse events
+ this._x3domRoot.addEventListener('mouseup', this.stop, false);
+ this._x3domRoot.addEventListener('mouseout', this.stop, false);
+ this._x3domRoot.addEventListener('mousemove', this.move, true);
+
+ if (!this._runtime.canvas.disableTouch) {
+ // mozilla touch events
+ this._x3domRoot.addEventListener('MozTouchDown', this.touchStartHandlerMoz, false);
+ this._x3domRoot.addEventListener('MozTouchMove', this.touchMoveHandlerMoz, true);
+ this._x3domRoot.addEventListener('MozTouchUp', this.touchEndHandlerMoz, false);
+ // w3c / apple touch events
+ this._x3domRoot.addEventListener('touchstart', this.touchStartHandler, false);
+ this._x3domRoot.addEventListener('touchmove', this.touchMoveHandler, true);
+ this._x3domRoot.addEventListener('touchend', this.touchEndHandler, false);
+ }
+ }
+};
+
+x3dom.Moveable.prototype.detachHandlers = function() {
+ // remove backref to <x3d> element
+ var iMove = this._x3domRoot._iMove;
+ if (iMove) {
+ for (var i=0, n=iMove.length; i<n; i++) {
+ if (iMove[i] == this) {
+ iMove.splice(i, 1);
+ break;
+ }
+ }
+ }
+
+ // mouse events
+ this._moveable.removeEventListener('mousedown', this.start, false);
+ this._moveable.removeEventListener('mouseover', this.over, false);
+ this._moveable.removeEventListener('mouseout', this.out, false);
+
+ if (iMove.length == 0) {
+ // more mouse events
+ this._x3domRoot.removeEventListener('mouseup', this.stop, false);
+ this._x3domRoot.removeEventListener('mouseout', this.stop, false);
+ this._x3domRoot.removeEventListener('mousemove', this.move, true);
+
+ if (!this._runtime.canvas.disableTouch) {
+ // touch events
+ this._x3domRoot.removeEventListener('MozTouchDown', this.touchStartHandlerMoz, false);
+ this._x3domRoot.removeEventListener('MozTouchMove', this.touchMoveHandlerMoz, true);
+ this._x3domRoot.removeEventListener('MozTouchUp', this.touchEndHandlerMoz, false);
+ // mozilla version
+ this._x3domRoot.removeEventListener('touchstart', this.touchStartHandler, false);
+ this._x3domRoot.removeEventListener('touchmove', this.touchMoveHandler, true);
+ this._x3domRoot.removeEventListener('touchend', this.touchEndHandler, false);
+ }
+ }
+
+ // finally remove backref to movable object
+ if (this._moveable._iMove)
+ delete this._moveable._iMove;
+};
+
+// calculate viewing plane
+x3dom.Moveable.prototype.calcViewPlane = function(origin) {
+ // init width and height
+ this._w = this._runtime.getWidth();
+ this._h = this._runtime.getHeight();
+
+ //bottom left of viewarea
+ var ray = this._runtime.getViewingRay(0, this._h - 1);
+ var r = ray.pos.add(ray.dir);
+
+ //bottom right of viewarea
+ ray = this._runtime.getViewingRay(this._w - 1, this._h - 1);
+ var s = ray.pos.add(ray.dir);
+
+ //top left of viewarea
+ ray = this._runtime.getViewingRay(0, 0);
+ var t = ray.pos.add(ray.dir);
+
+ this._uPlane = s.subtract(r).normalize();
+ this._vPlane = t.subtract(r).normalize();
+
+ if (arguments.length === 0)
+ this._pPlane = r;
+ else
+ this._pPlane = x3dom.fields.SFVec3f.copy(origin);
+};
+
+// helper method to obtain determinant
+x3dom.Moveable.prototype.det = function(mat) {
+ return mat[0][0] * mat[1][1] * mat[2][2] + mat[0][1] * mat[1][2] * mat[2][0] +
+ mat[0][2] * mat[2][1] * mat[1][0] - mat[2][0] * mat[1][1] * mat[0][2] -
+ mat[0][0] * mat[2][1] * mat[1][2] - mat[1][0] * mat[0][1] * mat[2][2];
+};
+
+// Translation along plane parallel to viewing plane E:x=p+t*u+s*v
+x3dom.Moveable.prototype.translateXY = function(l) {
+ var track = null;
+ var z = [], n = [];
+
+ for (var i = 0; i < 3; i++) {
+ z[i] = [];
+ n[i] = [];
+
+ z[i][0] = this._uPlane.at(i);
+ n[i][0] = z[i][0];
+
+ z[i][1] = this._vPlane.at(i);
+ n[i][1] = z[i][1];
+
+ z[i][2] = (l.pos.subtract(this._pPlane)).at(i);
+ n[i][2] = -l.dir.at(i);
+ }
+
+ // get intersection line-plane with Cramer's rule
+ var s = this.det(n);
+
+ if (s !== 0) {
+ var t = this.det(z) / s;
+ track = l.pos.addScaled(l.dir, t);
+ }
+
+ if (track) {
+ if (this._isect) {
+ // calc offset from first click position
+ track = track.subtract(this._isect);
+ }
+ track = track.add(this._translationOffset);
+ }
+
+ return track;
+};
+
+// Translation along picking ray
+x3dom.Moveable.prototype.translateZ = function(l, currY) {
+ var vol = this._runtime.getSceneBBox();
+
+ var sign = (currY < this._lastY) ? 1 : -1;
+ var fact = sign * (vol.max.subtract(vol.min)).length() / 100;
+
+ this._translationOffset = this._translationOffset.addScaled(l.dir, fact);
+
+ return this._translationOffset;
+};
+
+x3dom.Moveable.prototype.rotate = function(posX, posY) {
+ var twoPi = 2 * Math.PI;
+ var alpha = ((posY - this._lastY) * twoPi) / this._w;
+ var beta = ((posX - this._lastX) * twoPi) / this._h;
+
+ var q = x3dom.fields.Quaternion.axisAngle(this._uPlane, alpha);
+ var h = q.toMatrix();
+ this._rotationOffset = h.mult(this._rotationOffset);
+
+ q = x3dom.fields.Quaternion.axisAngle(this._vPlane, beta);
+ h = q.toMatrix();
+ this._rotationOffset = h.mult(this._rotationOffset);
+
+ var mat = this._rotationOffset.mult(x3dom.fields.SFMatrix4f.scale(this._scaleOffset));
+ var rot = new x3dom.fields.Quaternion(0, 0, 1, 0);
+ rot.setValue(mat);
+
+ return rot;
+};
+
+x3dom.Moveable.prototype.over = function(event) {
+ var that = this._iMove;
+
+ that._runtime.getCanvas().style.cursor = "crosshair";
+};
+
+x3dom.Moveable.prototype.out = function(event) {
+ var that = this._iMove;
+
+ if (!that._drag)
+ that._runtime.getCanvas().style.cursor = "pointer";
+};
+
+// start object movement, switch from navigation to interaction
+x3dom.Moveable.prototype.start = function(event) {
+ var that = this._iMove;
+
+ // use mouse button to distinguish between parallel or orthogonal movement or rotation
+ switch (that._mode) {
+ case "translation":
+ that._buttonState = (event.button == 4) ? 1 : (event.button & 3);
+ break;
+ case "rotation":
+ that._buttonState = 4;
+ break;
+ case "all":
+ default:
+ that._buttonState = event.button;
+ break;
+ }
+
+ if (!that._drag && that._buttonState) {
+ that._lastX = event.layerX;
+ that._lastY = event.layerY;
+
+ that._drag = true;
+
+ // temporarily disable navigation
+ that._navType = that._runtime.navigationType();
+ that._runtime.noNav();
+
+ // calc view-aligned plane through original pick position
+ that._isect = new x3dom.fields.SFVec3f(event.worldX, event.worldY, event.worldZ);
+ that.calcViewPlane(that._isect);
+
+ that._firstRay = that._runtime.getViewingRay(event.layerX, event.layerY);
+
+ var mTrans = that._moveable.getAttribute("translation");
+ that._matrixTrafo = null;
+
+ if (mTrans) {
+ that._translationOffset = x3dom.fields.SFVec3f.parse(mTrans);
+
+ var mRot = that._moveable.getAttribute("rotation");
+ mRot = mRot ? x3dom.fields.Quaternion.parseAxisAngle(mRot) : new x3dom.fields.Quaternion(0,0,1,0);
+ that._rotationOffset = mRot.toMatrix();
+
+ var mScal = that._moveable.getAttribute("scale");
+ that._scaleOffset = mScal ? x3dom.fields.SFVec3f.parse(mScal) : new x3dom.fields.SFVec3f(1, 1, 1);
+ }
+ else {
+ mTrans = that._moveable.getAttribute("matrix");
+
+ if (mTrans) {
+ that._matrixTrafo = x3dom.fields.SFMatrix4f.parse(mTrans).transpose();
+
+ var translation = new x3dom.fields.SFVec3f(0,0,0),
+ scaleFactor = new x3dom.fields.SFVec3f(1,1,1);
+ var rotation = new x3dom.fields.Quaternion(0,0,1,0),
+ scaleOrientation = new x3dom.fields.Quaternion(0,0,1,0);
+
+ that._matrixTrafo.getTransform(translation, rotation, scaleFactor, scaleOrientation);
+
+ //that._translationOffset = that._matrixTrafo.e3();
+ that._translationOffset = translation;
+ that._rotationOffset = rotation.toMatrix();
+ that._scaleOffset = scaleFactor;
+ }
+ else {
+ that._translationOffset = new x3dom.fields.SFVec3f(0, 0, 0);
+ that._rotationOffset = new x3dom.fields.SFMatrix4f();
+ that._scaleOffset = new x3dom.fields.SFVec3f(1, 1, 1);
+ }
+ }
+
+ that._runtime.getCanvas().style.cursor = "crosshair";
+ }
+};
+
+x3dom.Moveable.prototype.move = function(event) {
+ for (var i=0, n=this._iMove.length; i<n; i++) {
+ var that = this._iMove[i];
+
+ if (that._drag) {
+ var pos = that._runtime.mousePosition(event);
+ var ray = that._runtime.getViewingRay(pos[0], pos[1]);
+
+ var track = null;
+
+ // zoom with right mouse button (2), pan with left (1)
+ if (that._buttonState == 2)
+ track = that.translateZ(that._firstRay, pos[1]);
+ else if (that._buttonState == 1)
+ track = that.translateXY(ray);
+ else // middle button: 4
+ track = that.rotate(pos[0], pos[1]);
+
+ if (track) {
+ if (that._gridSize > 0 && that._buttonState != 4) {
+ var x = that._gridSize * Math.round(track.x / that._gridSize);
+ var y = that._gridSize * Math.round(track.y / that._gridSize);
+ var z = that._gridSize * Math.round(track.z / that._gridSize);
+ track = new x3dom.fields.SFVec3f(x, y, z);
+ }
+
+ if (!that._matrixTrafo) {
+ if (that._buttonState == 4) {
+ that._moveable.setAttribute("rotation", track.toAxisAngle().toString());
+ }
+ else {
+ that._moveable.setAttribute("translation", track.toString());
+ }
+ }
+ else {
+ if (that._buttonState == 4) {
+ that._matrixTrafo.setRotate(track);
+ }
+ else {
+ that._matrixTrafo.setTranslate(track);
+ }
+ that._moveable.setAttribute("matrix", that._matrixTrafo.toGL().toString());
+ }
+
+ if (that._callback) {
+ that._callback(that._moveable, track);
+ }
+ }
+
+ that._lastX = pos[0];
+ that._lastY = pos[1];
+ }
+ }
+};
+
+// stop object movement, switch from interaction to navigation
+x3dom.Moveable.prototype.stop = function(event) {
+ for (var i=0, n=this._iMove.length; i<n; i++) {
+ var that = this._iMove[i];
+
+ if (that._drag) {
+ that._lastX = event.layerX;
+ that._lastY = event.layerY;
+
+ that._isect = null;
+ that._drag = false;
+
+ // we're done, re-enable navigation
+ var navi = that._runtime.canvas.doc._scene.getNavigationInfo();
+ navi.setType(that._navType);
+
+ that._runtime.getCanvas().style.cursor = "pointer";
+ }
+ }
+};
+
+// TODO: impl. special (multi-)touch event stuff
+// === Touch Start (W3C) ===
+x3dom.Moveable.prototype.touchStartHandler = function (evt) {
+ evt.preventDefault();
+};
+
+// === Touch Start Moz (Firefox has other touch interface) ===
+x3dom.Moveable.prototype.touchStartHandlerMoz = function (evt) {
+ evt.preventDefault();
+};
+
+// === Touch Move ===
+x3dom.Moveable.prototype.touchMoveHandler = function (evt) {
+ evt.preventDefault();
+};
+
+// === Touch Move Moz ===
+x3dom.Moveable.prototype.touchMoveHandlerMoz = function (evt) {
+ evt.preventDefault();
+};
+
+// === Touch End ===
+x3dom.Moveable.prototype.touchEndHandler = function (evt) {
+ if (this._iMove.length) {
+ var that = this._iMove[0];
+ // mouse start code is called, but not stop
+ that.stop.apply(that._x3domRoot, [evt]);
+ }
+ evt.preventDefault();
+};
+
+// === Touch End Moz ===
+x3dom.Moveable.prototype.touchEndHandlerMoz = function (evt) {
+ if (this._iMove.length) {
+ var that = this._iMove[0];
+ that.stop.apply(that._x3domRoot, [evt]);
+ }
+ evt.preventDefault();
+};
+
+/*
+ * X3DOM JavaScript Library
+ * http://www.x3dom.org
+ *
+ * (C)2009 Fraunhofer IGD, Darmstadt, Germany
+ * Dual licensed under the MIT and GPL
+ *
+ * Based on code originally provided by
+ * Philip Taylor: http://philip.html5.org
+ */
+
+/**
+ * The canvas object wraps the HTML canvas x3dom draws
+ * @constructs x3dom.X3DCanvas
+ * @param {Object} [x3dElement] - x3d element rendering into the canvas
+ * @param {String} [canvasIdx] - id of HTML canvas
+ */
+x3dom.X3DCanvas = function(x3dElem, canvasIdx)
+{
+ var that = this;
+
+ /**
+ * The index of the HTML canvas
+ * @member {String} _canvasIdx
+ */
+ this._canvasIdx = canvasIdx;
+
+ /**
+ * Flag if flash is ready - needed for WebKit Browser
+ * @member {Boolean} isFlashReady
+ */
+ this.isFlashReady = false;
+
+ /**
+ * The X3D Element
+ * @member {X3DElement} x3dElem
+ */
+ this.x3dElem = x3dElem;
+
+ /**
+ * The current canvas dimensions
+ * @member {Array} _current_dim
+ */
+ this._current_dim = [0, 0];
+
+ // for FPS measurements
+ this.fps_t0 = new Date().getTime();
+ this.lastTimeFPSWasTaken = 0;
+ this.framesSinceLastTime = 0;
+
+ this.doc = null;
+
+ this.lastMousePos = { x: 0, y: 0 };
+ //try to determine behavior of certain DOMNodeInsertedEvent:
+ //IE11 dispatches one event for each node in an inserted subtree, other browsers use a single event per subtree
+ x3dom.caps.DOMNodeInsertedEvent_perSubtree = !(navigator.userAgent.indexOf('MSIE') != -1 ||
+ navigator.userAgent.indexOf('Trident') != -1 );
+
+ // allow listening for (size) changes
+ x3dElem.__setAttribute = x3dElem.setAttribute;
+
+ //adds setAttribute function for width and height to the X3D element
+ x3dElem.setAttribute = function(attrName, newVal)
+ {
+ this.__setAttribute(attrName, newVal);
+
+ switch(attrName) {
+
+ case "width":
+ that.canvas.setAttribute("width", newVal);
+ if (that.doc && that.doc._viewarea) {
+ that.doc._viewarea._width = parseInt(that.canvas.getAttribute("width"), 0);
+ that.doc.needRender = true;
+ }
+ break;
+
+ case "height":
+ that.canvas.setAttribute("height", newVal);
+ if (that.doc && that.doc._viewarea) {
+ that.doc._viewarea._height = parseInt(that.canvas.getAttribute("height"), 0);
+ that.doc.needRender = true;
+ }
+ break;
+
+ default:
+ break;
+ }
+ };
+
+
+ x3dom.caps.MOBILE = (navigator.appVersion.indexOf("Mobile") > -1);
+
+ this.backend = this.x3dElem.getAttribute('backend');
+ if (this.backend)
+ this.backend = this.backend.toLowerCase();
+ else
+ this.backend = 'none';
+
+ if (this.backend == 'flash') {
+ this.backend = 'flash';
+ this.canvas = this._createFlashObject(x3dElem);
+ if (this.canvas != null) {
+ this.canvas.parent = this;
+ this.gl = this._initFlashContext(this.canvas, this.flash_renderType);
+ } else {
+ this._createInitFailedDiv(x3dElem);
+ return;
+ }
+ } else {
+ this.canvas = this._createHTMLCanvas(x3dElem);
+ this.canvas.parent = this;
+ this.gl = this._initContext( this.canvas,
+ (this.backend.search("desktop") >= 0),
+ (this.backend.search("mobile") >= 0),
+ (this.backend.search("flashie") >= 0),
+ (this.backend.search("webgl2") >= 0));
+ this.backend = 'webgl';
+ if (this.gl == null)
+ {
+ x3dom.debug.logInfo("Fallback to Flash Renderer");
+ this.backend = 'flash';
+ this.canvas = this._createFlashObject(x3dElem);
+ if (this.canvas != null) {
+ this.canvas.parent = this;
+ this.gl = this._initFlashContext(this.canvas, this.flash_renderType);
+ } else {
+ this._createInitFailedDiv(x3dElem);
+ return;
+ }
+ }
+ }
+
+ x3dom.caps.BACKEND = this.backend;
+
+ var runtimeEnabled = x3dElem.getAttribute("runtimeEnabled");
+
+ if (runtimeEnabled !== null) {
+ this.hasRuntime = (runtimeEnabled.toLowerCase() == "true");
+ } else {
+ this.hasRuntime = x3dElem.hasRuntime;
+ }
+
+ if (this.gl === null) {
+ this.hasRuntime = false;
+ }
+
+ //States only needed for the webgl backend. flash has his own.
+ if (this.backend != "flash") {
+ this.showStat = x3dElem.getAttribute("showStat");
+
+ this.stateViewer = new x3dom.States(x3dElem);
+ if (this.showStat !== null && this.showStat == "true") {
+ this.stateViewer.display(true);
+ }
+
+ this.x3dElem.appendChild(this.stateViewer.viewer);
+ }
+
+ // progress bar
+ this.showProgress = x3dElem.getAttribute("showProgress");
+ this.progressDiv = this._createProgressDiv();
+ this.progressDiv.style.display = (this.showProgress !== null && this.showProgress == "true") ? "inline" : "none";
+ this.x3dElem.appendChild(this.progressDiv);
+
+ // touch visualization
+ this.showTouchpoints = x3dElem.getAttribute("showTouchpoints");
+ this.showTouchpoints = this.showTouchpoints ? !(this.showTouchpoints.toLowerCase() == "false") : true;
+ //this.showTouchpoints = this.showTouchpoints ? (this.showTouchpoints.toLowerCase() == "true") : false;
+
+ // disable touch events
+ this.disableTouch = x3dElem.getAttribute("disableTouch");
+ this.disableTouch = this.disableTouch ? (this.disableTouch.toLowerCase() == "true") : false;
+
+
+ if (this.canvas !== null && this.gl !== null && this.hasRuntime && this.backend !== "flash") {
+ // event handler for mouse interaction
+ this.canvas.mouse_dragging = false;
+ this.canvas.mouse_button = 0;
+ this.canvas.mouse_drag_x = 0;
+ this.canvas.mouse_drag_y = 0;
+
+ this.canvas.isMulti = false; // don't interfere with multi-touch
+
+ this.canvas.oncontextmenu = function(evt) {
+ evt.preventDefault();
+ evt.stopPropagation();
+ return false;
+ };
+
+ // TODO: handle context lost events properly
+ this.canvas.addEventListener("webglcontextlost", function(event) {
+ x3dom.debug.logError("WebGL context lost");
+ event.preventDefault();
+ }, false);
+
+ this.canvas.addEventListener("webglcontextrestored", function(event) {
+ x3dom.debug.logError("recover WebGL state and resources on context lost NYI");
+ event.preventDefault();
+ }, false);
+
+
+ // Mouse Events
+ this.canvas.addEventListener('mousedown', function (evt) {
+ if(!this.isMulti) {
+ this.focus();
+ this.classList.add('x3dom-canvas-mousedown');
+
+ switch(evt.button) {
+ case 0: this.mouse_button = 1; break; //left
+ case 1: this.mouse_button = 4; break; //middle
+ case 2: this.mouse_button = 2; break; //right
+ default: this.mouse_button = 0; break;
+ }
+
+ if (evt.shiftKey) { this.mouse_button = 1; }
+ if (evt.ctrlKey) { this.mouse_button = 4; }
+ if (evt.altKey) { this.mouse_button = 2; }
+
+ var pos = this.parent.mousePosition(evt);
+ this.mouse_drag_x = pos.x;
+ this.mouse_drag_y = pos.y;
+
+ this.mouse_dragging = true;
+
+ this.parent.doc.onMousePress(that.gl, this.mouse_drag_x, this.mouse_drag_y, this.mouse_button);
+ this.parent.doc.needRender = true;
+ }
+ }, false);
+
+ this.canvas.addEventListener('mouseup', function (evt) {
+ if(!this.isMulti) {
+ var prev_mouse_button = this.mouse_button;
+ this.classList.remove('x3dom-canvas-mousedown');
+
+ this.mouse_button = 0;
+ this.mouse_dragging = false;
+
+ this.parent.doc.onMouseRelease(that.gl, this.mouse_drag_x, this.mouse_drag_y, this.mouse_button, prev_mouse_button);
+ this.parent.doc.needRender = true;
+ }
+ }, false);
+
+ this.canvas.addEventListener('mouseover', function (evt) {
+ if(!this.isMulti) {
+ this.mouse_button = 0;
+ this.mouse_dragging = false;
+
+ this.parent.doc.onMouseOver(that.gl, this.mouse_drag_x, this.mouse_drag_y, this.mouse_button);
+ this.parent.doc.needRender = true;
+ }
+ }, false);
+
+ this.canvas.addEventListener('mouseout', function (evt) {
+ if(!this.isMulti) {
+ this.mouse_button = 0;
+ this.mouse_dragging = false;
+ this.classList.remove('x3dom-canvas-mousedown');
+
+ this.parent.doc.onMouseOut(that.gl, this.mouse_drag_x, this.mouse_drag_y, this.mouse_button);
+ this.parent.doc.needRender = true;
+ }
+ }, false);
+
+ this.canvas.addEventListener('dblclick', function (evt) {
+ if(!this.isMulti) {
+ this.mouse_button = 0;
+
+ var pos = this.parent.mousePosition(evt);
+ this.mouse_drag_x = pos.x;
+ this.mouse_drag_y = pos.y;
+
+ this.mouse_dragging = false;
+
+ this.parent.doc.onDoubleClick(that.gl, this.mouse_drag_x, this.mouse_drag_y);
+ this.parent.doc.needRender = true;
+ }
+ }, false);
+
+ this.canvas.addEventListener('mousemove', function (evt) {
+ if(!this.isMulti) {
+
+ var pos = this.parent.mousePosition(evt);
+
+ if ( pos.x != that.lastMousePos.x || pos.y != that.lastMousePos.y ) {
+ that.lastMousePos = pos;
+ if (evt.shiftKey) { this.mouse_button = 1; }
+ if (evt.ctrlKey) { this.mouse_button = 4; }
+ if (evt.altKey) { this.mouse_button = 2; }
+
+ this.mouse_drag_x = pos.x;
+ this.mouse_drag_y = pos.y;
+
+ if (this.mouse_dragging) {
+ this.parent.doc.onDrag(that.gl, this.mouse_drag_x, this.mouse_drag_y, this.mouse_button);
+ }
+ else {
+ this.parent.doc.onMove(that.gl, this.mouse_drag_x, this.mouse_drag_y, this.mouse_button);
+ }
+
+ this.parent.doc.needRender = true;
+
+ // deliberately different for performance reasons
+ evt.preventDefault();
+ evt.stopPropagation();
+ }
+ }
+ }, false);
+
+ this.canvas.addEventListener('DOMMouseScroll', function (evt) {
+ if(!this.isMulti) {
+ this.focus();
+
+ var originalY = this.parent.mousePosition(evt).y;
+
+ this.mouse_drag_y += 2 * evt.detail;
+
+ this.parent.doc.onWheel(that.gl, this.mouse_drag_x, this.mouse_drag_y, originalY);
+ this.parent.doc.needRender = true;
+
+ evt.preventDefault();
+ evt.stopPropagation();
+ }
+ }, false);
+
+ this.canvas.addEventListener('mousewheel', function (evt) {
+ if(!this.isMulti) {
+ this.focus();
+
+ var originalY = this.parent.mousePosition(evt).y;
+
+ this.mouse_drag_y -= 0.1 * evt.wheelDelta;
+
+ this.parent.doc.onWheel(that.gl, this.mouse_drag_x, this.mouse_drag_y, originalY);
+ this.parent.doc.needRender = true;
+
+ evt.preventDefault();
+ evt.stopPropagation();
+ }
+ }, false);
+
+
+ // Key Events
+ this.canvas.addEventListener('keypress', function (evt) {
+ var keysEnabled = this.parent.x3dElem.getAttribute("keysEnabled");
+ if (!keysEnabled || keysEnabled.toLowerCase() == "true") {
+ this.parent.doc.onKeyPress(evt.charCode);
+ }
+ this.parent.doc.needRender = true;
+ }, true);
+
+ // in webkit special keys are only handled on key-up
+ this.canvas.addEventListener('keyup', function (evt) {
+ var keysEnabled = this.parent.x3dElem.getAttribute("keysEnabled");
+ if (!keysEnabled || keysEnabled.toLowerCase() == "true") {
+ this.parent.doc.onKeyUp(evt.keyCode);
+ }
+ this.parent.doc.needRender = true;
+ }, true);
+
+ this.canvas.addEventListener('keydown', function (evt) {
+ var keysEnabled = this.parent.x3dElem.getAttribute("keysEnabled");
+ if (!keysEnabled || keysEnabled.toLowerCase() == "true") {
+ this.parent.doc.onKeyDown(evt.keyCode);
+ }
+ this.parent.doc.needRender = true;
+ }, true);
+
+
+ // Multitouch Events
+ var touches =
+ {
+ numTouches : 0,
+
+ firstTouchTime: new Date().getTime(),
+ firstTouchPoint: new x3dom.fields.SFVec2f(0,0),
+
+ lastPos : new x3dom.fields.SFVec2f(),
+ lastDrag : new x3dom.fields.SFVec2f(),
+
+ lastMiddle : new x3dom.fields.SFVec2f(),
+ lastSquareDistance : 0,
+ lastAngle : 0,
+ lastLayer : [],
+
+ examineNavType: 1,
+
+ calcAngle : function(vector)
+ {
+ var rotation = vector.normalize().dot(new x3dom.fields.SFVec2f(1,0));
+ rotation = Math.acos(rotation);
+
+ if(vector.y < 0)
+ rotation = Math.PI + (Math.PI - rotation);
+
+ return rotation;
+ },
+
+ disableTouch: this.disableTouch,
+ // set a marker in HTML so we can track the position of the finger visually
+ visMarker: this.showTouchpoints,
+ visMarkerBag: [],
+
+ visualizeTouches: function(evt)
+ {
+ if (!this.visMarker)
+ return;
+
+ var touchBag = [];
+ var marker = null;
+
+ for (var i=0; i<evt.touches.length; i++) {
+ var id = evt.touches[i].identifier || evt.touches[i].streamId;
+ if (!id) id = 0;
+
+ var index = this.visMarkerBag.indexOf(id);
+
+ if (index >= 0) {
+ marker = document.getElementById("visMarker" + id);
+
+ marker.style.left = (evt.touches[i].pageX) + "px";
+ marker.style.top = (evt.touches[i].pageY) + "px";
+ }
+ else {
+ marker = document.createElement("div");
+
+ marker.appendChild(document.createTextNode("#" + id));
+ marker.id = "visMarker" + id;
+ marker.className = "x3dom-touch-marker";
+ document.body.appendChild(marker);
+
+ index = this.visMarkerBag.length;
+ this.visMarkerBag[index] = id;
+ }
+
+ touchBag.push(id);
+ }
+
+ for (var j=this.visMarkerBag.length-1; j>=0; j--) {
+ var oldId = this.visMarkerBag[j];
+
+ if (touchBag.indexOf(oldId) < 0) {
+ this.visMarkerBag.splice(j, 1);
+ marker = document.getElementById("visMarker" + oldId);
+ document.body.removeChild(marker);
+ }
+ }
+ }
+ };
+
+ // Mozilla Touches (seems obsolete now...)
+ var mozilla_ids = [];
+
+ var mozilla_touches =
+ {
+ touches : [],
+ preventDefault : function() {}
+ };
+
+ // === Touch Start ===
+ var touchStartHandler = function(evt, doc)
+ {
+ this.isMulti = true;
+ evt.preventDefault();
+ touches.visualizeTouches(evt);
+
+ this.focus();
+
+ if (doc == null)
+ doc = this.parent.doc;
+
+ var navi = doc._scene.getNavigationInfo();
+
+ switch(navi.getType()) {
+ case "examine":
+ touches.examineNavType = 1;
+ break;
+ case "turntable":
+ touches.examineNavType = 2;
+ break;
+ default:
+ touches.examineNavType = 0;
+ break;
+ }
+
+ touches.lastLayer = [];
+
+ var i, pos;
+ for(i = 0; i < evt.touches.length; i++) {
+ pos = this.parent.mousePosition(evt.touches[i]);
+ touches.lastLayer.push([evt.touches[i].identifier, new x3dom.fields.SFVec2f(pos.x,pos.y)]);
+ }
+
+ if(touches.numTouches < 1 && evt.touches.length == 1) {
+
+ touches.numTouches = 1;
+ touches.lastDrag = new x3dom.fields.SFVec2f(evt.touches[0].screenX, evt.touches[0].screenY);
+ }
+ else if(touches.numTouches < 2 && evt.touches.length >= 2) {
+
+ touches.numTouches = 2;
+
+ var touch0 = new x3dom.fields.SFVec2f(evt.touches[0].screenX, evt.touches[0].screenY);
+ var touch1 = new x3dom.fields.SFVec2f(evt.touches[1].screenX, evt.touches[1].screenY);
+
+ var distance = touch1.subtract(touch0);
+ var middle = distance.multiply(0.5).add(touch0);
+ var squareDistance = distance.dot(distance);
+
+ touches.lastMiddle = middle;
+ touches.lastSquareDistance = squareDistance;
+ touches.lastAngle = touches.calcAngle(distance);
+
+ touches.lastPos = this.parent.mousePosition(evt.touches[0]);
+ }
+
+ // update scene bbox
+ doc._scene.updateVolume();
+
+ if (touches.examineNavType == 1) {
+ for(i = 0; i < evt.touches.length; i++) {
+ pos = this.parent.mousePosition(evt.touches[i]);
+ doc.onPick(that.gl, pos.x, pos.y);
+ doc._viewarea.prepareEvents(pos.x, pos.y, 1, "onmousedown");
+ doc._viewarea._pickingInfo.lastClickObj = doc._viewarea._pickingInfo.pickObj;
+ }
+ }
+ else if (evt.touches.length) {
+ pos = this.parent.mousePosition(evt.touches[0]);
+ doc.onMousePress(that.gl, pos.x, pos.y, 1); // 1 means left mouse button
+ }
+
+ doc.needRender = true;
+ };
+
+ var touchStartHandlerMoz = function(evt)
+ {
+ this.isMulti = true;
+ evt.preventDefault();
+
+ var new_id = true;
+ for(var i=0; i<mozilla_ids.length; ++i)
+ if(mozilla_ids[i] == evt.streamId)
+ new_id = false;
+
+ if(new_id == true) {
+ evt.identifier = evt.streamId;
+ mozilla_ids.push(evt.streamId);
+ mozilla_touches.touches.push(evt);
+ }
+ touchStartHandler(mozilla_touches, this.parent.doc);
+ };
+
+ // === Touch Move ===
+ var touchMoveHandler = function(evt, doc)
+ {
+ evt.preventDefault();
+ touches.visualizeTouches(evt);
+
+ if (doc == null)
+ doc = this.parent.doc;
+
+ var pos = null;
+ var rotMatrix = null;
+
+ var touch0, touch1, distance, middle, squareDistance, deltaMiddle, deltaZoom, deltaMove;
+
+ if (touches.examineNavType == 1) {
+ /*
+ if (doc._scene._vf.doPickPass && doc._scene._vf.pickMode.toLowerCase() !== "box") {
+ for(var i = 0; i < evt.touches.length; i++) {
+ pos = this.parent.mousePosition(evt.touches[i]);
+ doc.onPick(that.gl, pos.x, pos.y);
+
+ doc._viewarea.handleMoveEvt(pos.x, pos.y, 1);
+ }
+ }
+ */
+
+ // one finger: x/y rotation
+ if(evt.touches.length == 1) {
+ var currentDrag = new x3dom.fields.SFVec2f(evt.touches[0].screenX, evt.touches[0].screenY);
+
+ var deltaDrag = currentDrag.subtract(touches.lastDrag);
+ touches.lastDrag = currentDrag;
+
+ var mx = x3dom.fields.SFMatrix4f.rotationY(deltaDrag.x / 100);
+ var my = x3dom.fields.SFMatrix4f.rotationX(deltaDrag.y / 100);
+ rotMatrix = mx.mult(my);
+
+ doc.onMoveView(that.gl, null, rotMatrix);
+ }
+ // two fingers: scale, translation, rotation around view (z) axis
+ else if(evt.touches.length >= 2) {
+ touch0 = new x3dom.fields.SFVec2f(evt.touches[0].screenX, evt.touches[0].screenY);
+ touch1 = new x3dom.fields.SFVec2f(evt.touches[1].screenX, evt.touches[1].screenY);
+
+ distance = touch1.subtract(touch0);
+ middle = distance.multiply(0.5).add(touch0);
+ squareDistance = distance.dot(distance);
+
+ deltaMiddle = middle.subtract(touches.lastMiddle);
+ deltaZoom = squareDistance - touches.lastSquareDistance;
+
+ deltaMove = new x3dom.fields.SFVec3f(
+ deltaMiddle.x / screen.width, -deltaMiddle.y / screen.height,
+ deltaZoom / (screen.width * screen.height * 0.2));
+
+ var rotation = touches.calcAngle(distance);
+ var angleDelta = touches.lastAngle - rotation;
+ touches.lastAngle = rotation;
+
+ rotMatrix = x3dom.fields.SFMatrix4f.rotationZ(angleDelta);
+
+ touches.lastMiddle = middle;
+ touches.lastSquareDistance = squareDistance;
+
+ doc.onMoveView(that.gl, deltaMove, rotMatrix);
+ }
+ }
+ else if (evt.touches.length) {
+ if (touches.examineNavType == 2 && evt.touches.length >= 2) {
+ touch0 = new x3dom.fields.SFVec2f(evt.touches[0].screenX, evt.touches[0].screenY);
+ touch1 = new x3dom.fields.SFVec2f(evt.touches[1].screenX, evt.touches[1].screenY);
+
+ distance = touch1.subtract(touch0);
+ squareDistance = distance.dot(distance);
+ deltaZoom = (squareDistance - touches.lastSquareDistance) / (0.1 * (screen.width + screen.height));
+
+ touches.lastPos.y += deltaZoom;
+ touches.lastSquareDistance = squareDistance;
+
+ doc.onDrag(that.gl, touches.lastPos.x, touches.lastPos.y, 2);
+ }
+ else {
+ pos = this.parent.mousePosition(evt.touches[0]);
+
+ doc.onDrag(that.gl, pos.x, pos.y, 1);
+ }
+ }
+
+ doc.needRender = true;
+ };
+
+ var touchMoveHandlerMoz = function(evt)
+ {
+ evt.preventDefault();
+
+ for(var i=0; i<mozilla_ids.length; ++i)
+ if(mozilla_ids[i] == evt.streamId)
+ mozilla_touches.touches[i] = evt;
+
+ touchMoveHandler(mozilla_touches, this.parent.doc);
+ };
+
+ // === Touch end ===
+ var touchEndHandler = function(evt, doc)
+ {
+ this.isMulti = false;
+ evt.preventDefault();
+ touches.visualizeTouches(evt);
+
+ if (doc == null)
+ doc = this.parent.doc;
+
+ doc._viewarea._isMoving = false;
+
+ // reinit first finger for rotation
+ if (touches.numTouches == 2 && evt.touches.length == 1)
+ touches.lastDrag = new x3dom.fields.SFVec2f(evt.touches[0].screenX, evt.touches[0].screenY);
+
+ var dblClick = false;
+
+ if (evt.touches.length < 2) {
+ if (touches.numTouches == 1)
+ dblClick = true;
+ touches.numTouches = evt.touches.length;
+ }
+
+ if (touches.examineNavType == 1) {
+ for(var i = 0; i < touches.lastLayer.length; i++) {
+ var pos = touches.lastLayer[i][1];
+
+ doc.onPick(that.gl, pos.x, pos.y);
+
+ if (doc._scene._vf.pickMode.toLowerCase() !== "box") {
+ doc._viewarea.prepareEvents(pos.x, pos.y, 1, "onmouseup");
+ doc._viewarea._pickingInfo.lastClickObj = doc._viewarea._pickingInfo.pickObj;
+
+ // click means that mousedown _and_ mouseup were detected on same element
+ if (doc._viewarea._pickingInfo.pickObj &&
+ doc._viewarea._pickingInfo.pickObj ===
+ doc._viewarea._pickingInfo.lastClickObj) {
+
+ doc._viewarea.prepareEvents(pos.x, pos.y, 1, "onclick");
+ }
+ }
+ else {
+ var line = doc._viewarea.calcViewRay(pos.x, pos.y);
+ var isect = doc._scene.doIntersect(line);
+ var obj = line.hitObject;
+
+ if (isect && obj) {
+ doc._viewarea._pick.setValues(line.hitPoint);
+ doc._viewarea.checkEvents(obj, pos.x, pos.y, 1, "onclick");
+
+ x3dom.debug.logInfo("Hit '" + obj._xmlNode.localName + "/ " +
+ obj._DEF + "' at pos " + doc._viewarea._pick);
+ }
+ }
+ }
+
+ if (dblClick) {
+ var now = new Date().getTime();
+ var dist = touches.firstTouchPoint.subtract(touches.lastDrag).length();
+
+ if (dist < 18 && now - touches.firstTouchTime < 180)
+ doc.onDoubleClick(that.gl, 0, 0);
+
+ touches.firstTouchTime = now;
+ touches.firstTouchPoint = touches.lastDrag;
+ }
+ }
+ else if (touches.lastLayer.length) {
+ pos = touches.lastLayer[0][1];
+ doc.onMouseRelease(that.gl, pos.x, pos.y, 0, 1);
+ }
+
+ doc.needRender = true;
+ };
+
+ var touchEndHandlerMoz = function(evt)
+ {
+ this.isMulti = false;
+ evt.preventDefault();
+
+ var remove_index = -1;
+ for(var i=0; i<mozilla_ids.length; ++i)
+ if(mozilla_ids[i] == evt.streamId)
+ remove_index = i;
+
+ if(remove_index != -1)
+ {
+ mozilla_ids.splice(remove_index, 1);
+ mozilla_touches.touches.splice(remove_index, 1);
+ }
+
+ touchEndHandler(mozilla_touches, this.parent.doc);
+ };
+
+ if (!this.disableTouch)
+ {
+ // mozilla touch events (TODO: seem to be obsolete now, completely remove all code if no one complains!)
+ // However, touch in general seems to be broken if this flag is not set: dom.w3c_touch_events.enabled;10
+ //this.canvas.addEventListener('MozTouchDown', touchStartHandlerMoz, true);
+ //this.canvas.addEventListener('MozTouchMove', touchMoveHandlerMoz, true);
+ //this.canvas.addEventListener('MozTouchUp', touchEndHandlerMoz, true);
+
+ // w3c / apple touch events (in Chrome via chrome://flags)
+ this.canvas.addEventListener('touchstart', touchStartHandler, true);
+ this.canvas.addEventListener('touchmove', touchMoveHandler, true);
+ this.canvas.addEventListener('touchend', touchEndHandler, true);
+ }
+ }
+};
+
+//----------------------------------------------------------------------------------------------------------------------
+
+/**
+ * Creates the WebGL context and returns it
+ * @returns {WebGLContext} gl
+ * @param {HTMLCanvas} canvas
+ * @param {Boolean} forbidMobileShaders - no mobile shaders allowed
+ * @param {Boolean} forceMobileShaders - force mobile shaders
+ * @param {Boolean} forceFlashForIE - force flash backend for internet explorer
+ * @param {Boolean} tryWebGL2 - try to retrieve a WebGL2 context
+ */
+x3dom.X3DCanvas.prototype._initContext = function(canvas, forbidMobileShaders, forceMobileShaders, forceFlashForIE, tryWebGL2)
+{
+ x3dom.debug.logInfo("Initializing X3DCanvas for [" + canvas.id + "]");
+ var gl = x3dom.gfx_webgl(canvas, forbidMobileShaders, forceMobileShaders, tryWebGL2, this.x3dElem);
+
+ if (!gl)
+ {
+ x3dom.debug.logError("No 3D context found...");
+ this.x3dElem.removeChild(canvas);
+ return null;
+ }
+ else
+ {
+ var webglVersion = parseFloat(x3dom.caps.VERSION.match(/\d+\.\d+/)[0]);
+ if (webglVersion < 1.0) {
+ console.log(forceFlashForIE);
+ if (forceFlashForIE) {
+ x3dom.debug.logError("No valid 3D context found...");
+ this.x3dElem.removeChild(canvas);
+ return null;
+ } else {
+ x3dom.debug.logError("WebGL version " + x3dom.caps.VERSION +
+ " lacks important WebGL/GLSL features needed for shadows, special vertex attribute types, etc.!");
+ }
+ }
+ }
+
+ return gl;
+};
+
+//----------------------------------------------------------------------------------------------------------------------
+
+/**
+ * Creates the WebGL context and returns it
+ * @returns {WebGLContext} gl
+ * @param {HTMLCanvas} canvas - the HTMLCanvas
+ * @param {Object} renderType - the renderType for the Flash backend
+ */
+x3dom.X3DCanvas.prototype._initFlashContext = function(canvas, renderType) {
+ x3dom.debug.logInfo("Initializing X3DObject for [" + canvas.id + "]");
+ return x3dom.gfx_flash(canvas, renderType);
+};
+
+//----------------------------------------------------------------------------------------------------------------------
+
+/**
+ * Creates a param node and adds it to the target node's children
+ * @param {String} node - the target node
+ * @param {String} name - the name for the parameter
+ * @param {String} value - the value for the parameter
+ */
+x3dom.X3DCanvas.prototype.appendParam = function(node, name, value) {
+ var param = document.createElement('param');
+ param.setAttribute('name', name);
+ param.setAttribute('value', value);
+ node.appendChild( param );
+};
+
+//----------------------------------------------------------------------------------------------------------------------
+
+/**
+ * Tests if a file exists
+ * @returns {Boolean}
+ * @param {String} url - the url to be tested
+ */
+x3dom.X3DCanvas.prototype._fileExists = function(url) {
+ var xhr = new XMLHttpRequest();
+ try {
+ xhr.open("HEAD", url, false);
+ xhr.send(null);
+ return (xhr.status != 404);
+ } catch(e) { return true; }
+};
+
+//----------------------------------------------------------------------------------------------------------------------
+
+/**
+ * Detects if flash is available
+ * @returns {Boolean}
+ * @param {String} required - required version
+ * @param {String} max - maximal compatible version
+ */
+x3dom.X3DCanvas.prototype._detectFlash = function(required, max)
+{
+ var required_version = required;
+ var max_version = max;
+ var available_version = 0;
+
+ /* this section is for NS, Mozilla, Firefox and similar Browsers */
+ if(typeof(navigator.plugins["Shockwave Flash"]) == "object")
+ {
+ var description = navigator.plugins["Shockwave Flash"].description;
+ available_version = description.substr(16, (description.indexOf(".", 16) - 16));
+ }
+ else if(typeof(ActiveXObject) == "function") {
+ for(var i = 10; i < (max_version + 1); i ++) {
+ try {
+ if(typeof(new ActiveXObject("ShockwaveFlash.ShockwaveFlash." + i)) == "object") {
+ available_version = i+1;
+ }
+ }
+ catch(error){}
+ }
+ }
+
+ return [available_version, required_version];
+};
+
+//----------------------------------------------------------------------------------------------------------------------
+
+/**
+ * Creates a div to inform the user that the initialization failed
+ * @param {String} x3dElem - the X3D element
+ */
+x3dom.X3DCanvas.prototype._createInitFailedDiv = function(x3dElem) {
+ var div = document.createElement('div');
+ div.setAttribute("id", "x3dom-create-init-failed");
+ div.style.width = x3dElem.getAttribute("width");
+ div.style.height = x3dElem.getAttribute("height");
+ div.style.backgroundColor = "#C00";
+ div.style.color = "#FFF";
+ div.style.fontSize = "20px";
+ div.style.fontWidth = "bold";
+ div.style.padding = "10px 10px 10px 10px";
+ div.style.display = "inline-block";
+ div.style.fontFamily = "Helvetica";
+ div.style.textAlign = "center";
+
+ div.appendChild(document.createTextNode('Your Browser does not support X3DOM'));
+ div.appendChild(document.createElement('br'));
+ div.appendChild(document.createTextNode('Read more about Browser support on:'));
+ div.appendChild(document.createElement('br'));
+
+ var link = document.createElement('a');
+ link.setAttribute('href', 'http://www.x3dom.org/?page_id=9');
+ link.appendChild( document.createTextNode('X3DOM | Browser Support'));
+ div.appendChild(link);
+
+ // check if "altImg" is specified on x3d element and if so use it as background
+ var altImg = x3dElem.getAttribute("altImg") || null;
+ if (altImg) {
+ var altImgObj = new Image();
+ altImgObj.src = altImg;
+ div.style.backgroundImage = "url("+altImg+")";
+ div.style.backgroundRepeat = "no-repeat";
+ div.style.backgroundPosition = "50% 50%";
+ }
+
+ x3dElem.appendChild(div);
+
+ x3dom.debug.logError("Your Browser does not support X3DOM!");
+};
+
+//----------------------------------------------------------------------------------------------------------------------
+
+/**
+ * Creates the flash object used as render target
+ * @returns {Object} - the flash object
+ * @param {HTMLNode} x3dElem - the X3D root node
+ */
+x3dom.X3DCanvas.prototype._createFlashObject = function (x3dElem) {
+
+ var result = this._detectFlash(11, 11);
+
+ if (!result[0] || result[0] < result[1]) {
+ return null;
+ } else {
+
+ x3dom.debug.logInfo("Creating FlashObject for (X)3D element...");
+
+ //Get X3D-Element ID
+ var id = this.x3dElem.getAttribute("id");
+ if (id !== null) {
+ id = "x3dom-" + id + "-object";
+ } else {
+ var index = new Date().getTime();
+ id = "x3dom-" + index + "-object";
+ }
+
+ //Get SWFPath
+ var swf_path = this.x3dElem.getAttribute("swfpath");
+ if (swf_path === null) {
+ swf_path = "x3dom.swf";
+ }
+
+ if (!this._fileExists(swf_path)) {
+ var version;
+
+ //No version info or a dev string?
+ if (x3dom.versionInfo === undefined || x3dom.versionInfo.version.indexOf('dev') != -1) //use dev version
+ {
+ version = "dev";
+ }
+ //Stable version?
+ else
+ {
+ version = x3dom.versionInfo.version;
+
+ //If version ends with ".0" (modification number), remove this part from path to download folder
+ var modification = version.substr(version.length-1);
+ if(modification == 0) {
+ version = version.substr(0, 3);
+ }
+ }
+
+ swf_path = "http://www.x3dom.org/download/" + version + "/x3dom.swf";
+
+ x3dom.debug.logWarning("Can't find local x3dom.swf (" + version + "). X3DOM now using the online version from x3dom.org." +
+ "The online version needs a <a href='http://examples.x3dom.org/crossdomain.xml'>crossdomain.xml</a> " +
+ "file in the root directory of your domain to access textures");
+ }
+
+ //Get width from x3d-Element or set default
+ var width = this.x3dElem.getAttribute("width");
+ var idx = -1;
+ if (width == null) {
+ width = 550;
+ } else {
+ idx = width.indexOf("px");
+ if (idx != -1) {
+ width = width.substr(0, idx);
+ }
+ }
+ //Get height from x3d-Element or set default
+ var height = this.x3dElem.getAttribute("height");
+ if (height == null) {
+ height = 400;
+ } else {
+ idx = height.indexOf("px");
+ if (idx != -1) {
+ height = height.substr(0, idx);
+ }
+ }
+
+ //Get flash render type
+ var renderType = this.x3dElem.getAttribute("flashrenderer");
+ if (renderType == null) {
+ this.flash_renderType = "forward";
+ } else {
+ this.flash_renderType = "deferred";
+ }
+
+ var obj = document.createElement('object');
+ obj.setAttribute('width', '100%');
+ obj.setAttribute('height', '100%');
+ obj.setAttribute('id', id);
+
+ //Check for xhtml
+ if (!document.doctype || document.doctype && document.doctype.publicId && document.doctype.publicId.search(/DTD XHTML/i) != -1) {
+ x3dom.debug.logWarning("Flash backend doesn't like XHTML, please use HTML5!");
+ obj.setAttribute('style', 'width:' + width + 'px; height:' + height + 'px;');
+ } else {
+ if (x3dElem.getAttribute('style') == null) {
+ x3dElem.setAttribute('style', 'width:' + width + 'px; height:' + height + 'px;');
+ }
+ }
+
+ this.appendParam(obj, 'menu', 'false');
+ this.appendParam(obj, 'quality', 'high');
+ this.appendParam(obj, 'wmode', 'direct');
+ this.appendParam(obj, 'allowScriptAccess', 'always');
+ this.appendParam(obj, 'flashvars', 'canvasIdx=' + this._canvasIdx + '&renderType=' + this.flash_renderType);
+ this.appendParam(obj, 'movie', swf_path);
+
+ if (navigator.appName == "Microsoft Internet Explorer") {
+ x3dElem.appendChild(obj);
+ obj.setAttribute('classid', 'clsid:d27cdb6e-ae6d-11cf-96b8-444553540000');
+ } else {
+ obj.setAttribute('type', 'application/x-shockwave-flash');
+ obj.setAttribute('data', swf_path);
+ x3dElem.appendChild(obj);
+ }
+
+ return obj;
+ }
+};
+
+//----------------------------------------------------------------------------------------------------------------------
+
+/**
+ * Creates the HTML canvas used as render target
+ * @returns {HTMLCanvas} - the created canvas
+ * @param {HTMLNode} x3dElem - the X3D root node
+ */
+x3dom.X3DCanvas.prototype._createHTMLCanvas = function(x3dElem)
+{
+ x3dom.debug.logInfo("Creating canvas for (X)3D element...");
+ var canvas = document.createElement('canvas');
+ canvas.setAttribute("class", "x3dom-canvas");
+
+ // check if user wants to style the X3D element
+ var userStyle = x3dElem.getAttribute("style");
+ if (userStyle) {
+ x3dom.debug.logInfo("Inline X3D styles detected");
+ }
+
+ // check if user wants to attach events to the X3D element
+ var evtArr = [
+ "onmousedown",
+ "onmousemove",
+ "onmouseout",
+ "onmouseover",
+ "onmouseup",
+ "onclick",
+ "ondblclick",
+ "onkeydown",
+ "onkeypress",
+ "onkeyup",
+
+ // w3c touch: http://www.w3.org/TR/2011/WD-touch-events-20110505/
+ "ontouchstart",
+ "ontouchmove",
+ "ontouchend",
+ "ontouchcancel",
+ "ontouchleave",
+ "ontouchenter",
+
+ // mozilla touch
+ //"onMozTouchDown",
+ //"onMozTouchMove",
+ //"onMozTouchUp",
+
+ // drag and drop, requires 'draggable' source property set true (usually of an img)
+ "ondragstart",
+ "ondrop",
+ "ondragover"
+ ];
+
+ // TODO; handle attribute event handlers dynamically during runtime
+ //this step is necessary because of some weird behavior in some browsers:
+ //we need a canvas element on startup to make every callback (e.g., 'onmousemove') work,
+ //which was previously set for the canvas' outer elements
+ for (var i=0; i < evtArr.length; i++)
+ {
+ var evtName = evtArr[i];
+ var userEvt = x3dElem.getAttribute(evtName);
+ if (userEvt) {
+ x3dom.debug.logInfo(evtName +", "+ userEvt);
+
+ canvas.setAttribute(evtName, userEvt);
+
+ //remove the event attribute from the X3D element to prevent duplicate callback invocation
+ x3dElem.removeAttribute(evtName);
+ }
+ }
+
+ var userProp = x3dElem.getAttribute("draggable");
+ if (userProp) {
+ x3dom.debug.logInfo("draggable=" + userProp);
+ canvas.setAttribute("draggable", userProp);
+ }
+
+ // workaround since one cannot find out which handlers are registered
+ if (!x3dElem.__addEventListener && !x3dElem.__removeEventListener)
+ {
+ x3dElem.__addEventListener = x3dElem.addEventListener;
+ x3dElem.__removeEventListener = x3dElem.removeEventListener;
+
+ // helpers to propagate the element's listeners
+ x3dElem.addEventListener = function(type, func, phase) {
+ var j, found = false;
+ for (j=0; j < evtArr.length && !found; j++) {
+ if (evtArr[j] === type) {
+ found = true;
+ }
+ }
+
+ if (found) {
+ x3dom.debug.logInfo('addEventListener for div.on' + type);
+ canvas.addEventListener(type, func, phase);
+ } else {
+ x3dom.debug.logInfo('addEventListener for X3D.on' + type);
+ this.__addEventListener(type, func, phase);
+ }
+ };
+
+ x3dElem.removeEventListener = function(type, func, phase) {
+ var j, found = false;
+ for (j=0; j<evtArr.length && !found; j++) {
+ if (evtArr[j] === type) {
+ found = true;
+ }
+ }
+
+ if (found) {
+ x3dom.debug.logInfo('removeEventListener for div.on' + type);
+ canvas.removeEventListener(type, func, phase);
+ } else {
+ x3dom.debug.logInfo('removeEventListener for X3D.on' + type);
+ this.__removeEventListener(type, func, phase);
+ }
+ };
+ }
+
+ x3dElem.appendChild(canvas);
+
+ // If the X3D element has an id attribute, append "_canvas"
+ // to it and and use that as the id for the canvas
+ var id = x3dElem.getAttribute("id");
+ if (id !== null) {
+ canvas.id = "x3dom-" + id + "-canvas";
+ } else {
+ // If the X3D element does not have an id... do what?
+ // For now check the date for creating a (hopefully) unique id
+ var index = new Date().getTime();
+ canvas.id = "x3dom-" + index + "-canvas";
+ }
+
+ // Apply the width and height of the X3D element to the canvas
+ var w, h;
+
+ if ((w = x3dElem.getAttribute("width")) !== null) {
+ //Attention: pbuffer dim is _not_ derived from style attribs!
+ if (w.indexOf("%") >= 0) {
+ x3dom.debug.logWarning("The width attribute is to be specified in pixels not in percent.");
+ }
+ canvas.style.width = w;
+ canvas.setAttribute("width", w);
+ }
+
+ if ((h = x3dElem.getAttribute("height")) !== null) {
+ //Attention: pbuffer dim is _not_ derived from style attribs!
+ if (h.indexOf("%") >= 0) {
+ x3dom.debug.logWarning("The height attribute is to be specified in pixels not in percent.");
+ }
+ canvas.style.height = h;
+ canvas.setAttribute("height", h);
+ }
+
+ // http://snook.ca/archives/accessibility_and_usability/elements_focusable_with_tabindex
+ canvas.setAttribute("tabindex", "0");
+ // canvas.focus(); ???why - it is necessary - makes touch events break???
+
+ return canvas;
+};
+
+/**
+ * Watches for a resize of the canvas and sets the current dimensions
+ */
+x3dom.X3DCanvas.prototype._watchForResize = function() {
+
+ var new_dim = [
+ parseInt(x3dom.getStyle(this.canvas, "width")),
+ parseInt(x3dom.getStyle(this.canvas, "height"))
+ ];
+
+ if ((this._current_dim[0] != new_dim[0]) || (this._current_dim[1] != new_dim[1])) {
+ this._current_dim = new_dim;
+ this.x3dElem.setAttribute("width", new_dim[0]+"px");
+ this.x3dElem.setAttribute("height", new_dim[1]+"px");
+ }
+};
+
+//----------------------------------------------------------------------------------------------------------------------
+
+/**
+ * Creates the div for progression visualization
+ */
+x3dom.X3DCanvas.prototype._createProgressDiv = function() {
+ var progressDiv = document.createElement('div');
+ progressDiv.setAttribute("class", "x3dom-progress");
+
+ var _text = document.createElement('strong');
+ _text.appendChild(document.createTextNode('Loading...'));
+ progressDiv.appendChild(_text);
+
+ var _inner = document.createElement('span');
+ _inner.setAttribute('style', "width: 25%;");
+ _inner.appendChild(document.createTextNode(' ')); // this needs to be a protected whitespace
+ progressDiv.appendChild(_inner);
+
+ progressDiv.oncontextmenu = progressDiv.onmousedown = function(evt) {
+ evt.preventDefault();
+ evt.stopPropagation();
+ return false;
+ };
+ return progressDiv;
+};
+
+//----------------------------------------------------------------------------------------------------------------------
+
+/** Helper that converts a point from node coordinates to page coordinates
+ FIXME: does NOT work when x3dom.css is not included so that x3d element is not floating
+ */
+x3dom.X3DCanvas.prototype.mousePosition = function(evt)
+{
+ var x = 0, y = 0;
+
+ if ( "getBoundingClientRect" in document.documentElement ) {
+ var elem = evt.target.offsetParent; // should be x3dElem
+ var box = elem.getBoundingClientRect();
+
+ var scrollLeft = window.pageXOffset || document.documentElement.scrollLeft;
+ var scrollTop = window.pageYOffset || document.documentElement.scrollTop;
+
+ var compStyle = document.defaultView.getComputedStyle(elem, null);
+
+ var paddingLeft = parseFloat(compStyle.getPropertyValue('padding-left'));
+ var borderLeftWidth = parseFloat(compStyle.getPropertyValue('border-left-width'));
+
+ var paddingTop = parseFloat(compStyle.getPropertyValue('padding-top'));
+ var borderTopWidth = parseFloat(compStyle.getPropertyValue('border-top-width'));
+
+ x = Math.round(evt.pageX - (box.left + paddingLeft + borderLeftWidth + scrollLeft));
+ y = Math.round(evt.pageY - (box.top + paddingTop + borderTopWidth + scrollTop));
+ }
+ else {
+ x3dom.debug.logError('NO getBoundingClientRect');
+ }
+
+ return new x3dom.fields.SFVec2f(x, y);
+};
+
+//----------------------------------------------------------------------------------------------------------------------
+
+/** Is called in the main loop after every frame
+ */
+x3dom.X3DCanvas.prototype.tick = function()
+{
+ var runtime = this.x3dElem.runtime;
+ var d = new Date().getTime();
+ var diff = d - this.lastTimeFPSWasTaken;
+
+ var fps = 1000.0 / (d - this.fps_t0);
+ this.fps_t0 = d;
+
+ // update routes and stuff
+ this.doc.advanceTime(d / 1000.0);
+ var animD = new Date().getTime() - d;
+
+ if (this.doc.needRender) {
+ // calc average frames per second
+ if (diff >= 1000) {
+ runtime.fps = this.framesSinceLastTime / (diff / 1000.0);
+ runtime.addMeasurement('FPS', runtime.fps);
+
+ this.framesSinceLastTime = 0;
+ this.lastTimeFPSWasTaken = d;
+ }
+ this.framesSinceLastTime++;
+
+ runtime.addMeasurement('ANIM', animD);
+
+ if (runtime.isReady == false) {
+ runtime.ready();
+ runtime.isReady = true;
+ }
+
+ runtime.enterFrame();
+
+ if (this.backend == 'flash') {
+ if (this.isFlashReady) {
+ this.canvas.setFPS({fps: fps});
+
+ this.doc.needRender = false;
+ this.doc.render(this.gl);
+ }
+ }
+ else {
+ // picking might require another pass
+ this.doc.needRender = false;
+ this.doc.render(this.gl);
+
+ if (!this.doc._scene._vf.doPickPass)
+ runtime.removeMeasurement('PICKING');
+ }
+
+ runtime.exitFrame();
+ }
+
+ if (this.progressDiv) {
+ if (this.doc.downloadCount > 0) {
+ runtime.addInfo("#LOADS:", this.doc.downloadCount);
+ } else {
+ runtime.removeInfo("#LOADS:");
+ }
+
+ if (this.doc.properties.getProperty("showProgress") !== 'false') {
+ if (this.progressDiv) {
+ this.progressDiv.childNodes[0].textContent = 'Loading: ' + (+this.doc.downloadCount);
+ if (this.doc.downloadCount > 0) {
+ this.progressDiv.style.display = 'inline';
+ } else {
+ this.progressDiv.style.display = 'none';
+ }
+ }
+ } else {
+ this.progressDiv.style.display = 'none';
+ }
+ }
+};
+
+//----------------------------------------------------------------------------------------------------------------------
+
+/** Loads the given @p uri.
+ * @param uri can be a uri or an X3D node
+ * @param sceneElemPos
+ * @param settings properties
+ */
+x3dom.X3DCanvas.prototype.load = function(uri, sceneElemPos, settings) {
+ this.doc = new x3dom.X3DDocument(this.canvas, this.gl, settings);
+ var x3dCanvas = this;
+
+ this.doc.onload = function () {
+ //x3dom.debug.logInfo("loaded '" + uri + "'");
+
+ if (x3dCanvas.hasRuntime) {
+
+ // requestAnimationFrame https://cvs.khronos.org/svn/repos/registry/trunk/public/webgl/sdk/demos/common/webgl-utils.js
+ (function mainloop(){
+ if (x3dCanvas.doc && x3dCanvas.x3dElem.runtime) {
+ x3dCanvas._watchForResize();
+ x3dCanvas.tick();
+ window.requestAnimFrame(mainloop, x3dCanvas);
+ }
+ })();
+
+ } else {
+ x3dCanvas.tick();
+ }
+ };
+
+ this.x3dElem.render = function() {
+ if (x3dCanvas.hasRuntime) {
+ x3dCanvas.doc.needRender = true;
+ } else {
+ x3dCanvas.doc.render(x3dCanvas.gl);
+ }
+ };
+
+ this.x3dElem.context = x3dCanvas.gl.ctx3d;
+
+ this.doc.onerror = function () {
+ alert('Failed to load X3D document');
+ };
+
+ this.doc.load(uri, sceneElemPos);
+};
+
+/*
+ * X3DOM JavaScript Library
+ * http://www.x3dom.org
+ *
+ * (C)2009 Fraunhofer IGD, Darmstadt, Germany
+ * Dual licensed under the MIT and GPL
+ *
+ * Based on code originally provided by
+ * Philip Taylor: http://philip.html5.org
+ */
+
+
+/**
+ * Class: x3dom.runtime
+ *
+ * Runtime proxy object to get and set runtime parameters. This object
+ * is attached to each X3D element and can be used in the following manner:
+ *
+ * > var e = document.getElementById('the_x3delement');
+ * > e.runtime.showAll();
+ * > e.runtime.resetView();
+ * > ...
+ */
+
+// Global runtime
+/**
+ * Namespace container for Runtime module
+ * @namespace x3dom.runtime
+ */
+x3dom.runtime = {};
+
+/** c'tor */
+x3dom.Runtime = function(doc, canvas) {
+ this.doc = doc;
+ this.canvas = canvas;
+
+ this.config = {};
+ this.isReady = false;
+
+ this.fps = 0;
+
+ this.states = { measurements: [], infos: [] };
+};
+
+
+x3dom.Runtime.prototype.addMeasurement = function (title, value) {
+ this.states.measurements[title] = value;
+};
+
+x3dom.Runtime.prototype.removeMeasurement = function (title) {
+ if (this.states.measurements[title]) {
+ delete this.states.measurements[title];
+ }
+};
+
+x3dom.Runtime.prototype.addInfo = function (title, value) {
+ this.states.infos[title] = value;
+};
+
+x3dom.Runtime.prototype.removeInfo = function (title) {
+ delete this.states.infos[title];
+};
+
+
+x3dom.Runtime.prototype.initialize = function(doc, canvas) {
+ this.doc = doc;
+ this.canvas = canvas;
+
+ // place to hold configuration data, i.e. flash backend path, etc.
+ // format and structure needs to be decided.
+ this.config = {};
+ this.isReady = false;
+
+ this.fps = 0;
+};
+
+
+/**
+ * APIFunction: noBackendFound
+ *
+ * This method is called once the system initialized and is not ready to
+ * render the first time because there is no backend found. By default this
+ * method noop. You can however override it with your own implementation.
+ *
+ * > x3dom.runtime.noBackendFound = function() {
+ * > alert("Dingel Dingel Ding Dong...");
+ * > }
+ *
+ * It is important to create this override before the document onLoad event has
+ * fired. Therefore putting it directly under the inclusion of x3dom.js is the
+ * preferred way to ensure overloading of this function.
+ */
+x3dom.Runtime.prototype.noBackendFound = function() {
+ x3dom.debug.logInfo('No backend found. Unable to render.');
+};
+
+/**
+ * APIFunction: ready
+ *
+ * This method is called once the system initialized and is ready to render
+ * the first time. It is therefore possible to execute custom
+ * action by overriding this method in your code:
+ *
+ * > x3dom.runtime.ready = function() {
+ * > alert("About to render something the first time");
+ * > }
+ *
+ * It is important to create this override before the document onLoad event has fired.
+ * Therefore putting it directly under the inclusion of x3dom.js is the preferred
+ * way to ensure overloading of this function.
+ *
+ * Parameters:
+ * element - The x3d element this handler is acting upon
+ */
+x3dom.Runtime.prototype.ready = function() {
+ x3dom.debug.logInfo('System ready.');
+};
+
+/**
+ * APIFunction: enterFrame
+ *
+ * This method is called just before the next frame is
+ * rendered. It is therefore possible to execute custom
+ * action by overriding this method in your code:
+ *
+ * > var element = document.getElementById('my_element');
+ * > element.runtime.enterFrame = function() {
+ * alert('hello custom enter frame');
+ * };
+ *
+ * If you have more than one X3D element in your HTML
+ * During initialization, just after ready() executed and before the very first frame
+ * is rendered, only the global override of this method works. If you need to execute
+ * code before the first frame renders, it is therefore best to use the ready()
+ * function instead.
+ *
+ * Parameters:
+ * element - The x3d element this handler is acting upon
+ */
+x3dom.Runtime.prototype.enterFrame = function() {
+ //x3dom.debug.logInfo('Render frame imminent');
+ // to be overwritten by user
+};
+
+/**
+ * APIFunction: exitFrame
+ *
+ * This method is called just after the current frame was
+ * rendered. It is therefore possible to execute custom
+ * action by overriding this method in your code:
+ *
+ * > var element = document.getElementById('my_element');
+ * > element.runtime.exitFrame = function() {
+ * alert('hello custom exit frame');
+ * };
+ *
+ * Parameters:
+ * element - The x3d element this handler is acting upon
+ */
+x3dom.Runtime.prototype.exitFrame = function() {
+ //x3dom.debug.logInfo('Render frame finished');
+ // to be overwritten by user
+};
+
+/**
+ * APIFunction: triggerRedraw
+ *
+ * triggers a redraw of the scene
+ *
+ */
+x3dom.Runtime.prototype.triggerRedraw = function() {
+ this.canvas.doc.needRender = true;
+};
+
+/**
+ * APIFunction: getActiveBindable
+ *
+ * Returns the currently active bindable DOM element of the given type.
+ * typeName must be a valid Bindable node (e.g. Viewpoint, Background, etc.).
+ *
+ * For example:
+ *
+ * > var element, bindable;
+ * > element = document.getElementById('the_x3delement');
+ * > bindable = element.runtime.getActiveBindable('background');
+ * > bindable.setAttribute('bind', 'false');
+ *
+ * Parameters:
+ * typeName - Bindable type name
+ *
+ * Returns:
+ * The active DOM element
+ */
+x3dom.Runtime.prototype.getActiveBindable = function(typeName) {
+ var stacks;
+ var i, current, result;
+ var type;
+
+ stacks = this.canvas.doc._bindableBag._stacks;
+ result = [];
+
+ type = x3dom.nodeTypesLC[typeName.toLowerCase()];
+
+ if (!type) {
+ x3dom.debug.logError('No node of type "' + typeName + '" found.');
+ return null;
+ }
+
+ for (i=0; i < stacks.length; i++) {
+ current = stacks[i].getActive();
+ if (current._xmlNode !== undefined && x3dom.isa(current, type) ) {
+ result.push(current);
+ }
+ }
+ return result[0] ? result[0]._xmlNode : null;
+};
+
+/**
+ * APIFunction: nextView
+ *
+ * Navigates tho the next viewpoint
+ *
+ */
+x3dom.Runtime.prototype.nextView = function() {
+ var stack = this.canvas.doc._scene.getViewpoint()._stack;
+ if (stack) {
+ stack.switchTo('next');
+ } else {
+ x3dom.debug.logError('No valid ViewBindable stack.');
+ }
+};
+
+/**
+ * APIFunction: prevView
+ *
+ * Navigates tho the previous viewpoint
+ *
+ */
+x3dom.Runtime.prototype.prevView = function() {
+ var stack = this.canvas.doc._scene.getViewpoint()._stack;
+ if (stack) {
+ stack.switchTo('prev');
+ } else {
+ x3dom.debug.logError('No valid ViewBindable stack.');
+ }
+};
+
+/**
+ * Function: viewpoint
+ *
+ * Returns the current viewpoint.
+ *
+ * Returns:
+ * The viewpoint
+ */
+x3dom.Runtime.prototype.viewpoint = function() {
+ return this.canvas.doc._scene.getViewpoint();
+};
+
+/**
+ * Function: viewMatrix
+ *
+ * Returns the current view matrix.
+ *
+ * Returns:
+ * Matrix object
+ */
+x3dom.Runtime.prototype.viewMatrix = function() {
+ return this.canvas.doc._viewarea.getViewMatrix();
+};
+
+/**
+ * Function: projectionMatrix
+ *
+ * Returns the current projection matrix.
+ *
+ * Returns:
+ * Matrix object
+ */
+x3dom.Runtime.prototype.projectionMatrix = function() {
+ return this.canvas.doc._viewarea.getProjectionMatrix();
+};
+
+/**
+ * Function: getWorldToCameraCoordinatesMatrix
+ *
+ * Returns the current world to camera coordinates matrix.
+ *
+ * Returns:
+ * Matrix object
+ */
+x3dom.Runtime.prototype.getWorldToCameraCoordinatesMatrix = function() {
+ return this.canvas.doc._viewarea.getWCtoCCMatrix();
+};
+
+/**
+ * Function: getCameraToWorldCoordinatesMatrix
+ *
+ * Returns the current camera to world coordinates matrix.
+ *
+ * Returns:
+ * Matrix object
+ */
+x3dom.Runtime.prototype.getCameraToWorldCoordinatesMatrix = function() {
+ return this.canvas.doc._viewarea.getCCtoWCMatrix();
+};
+
+/**
+ * Function: getViewingRay
+ *
+ * Returns the viewing ray for a given (x, y) position.
+ *
+ * Returns:
+ * Ray object
+ */
+x3dom.Runtime.prototype.getViewingRay = function(x, y) {
+ return this.canvas.doc._viewarea.calcViewRay(x, y);
+};
+
+/**
+ * Function: shootRay
+ *
+ * Returns pickPosition, pickNormal, and pickObject for a given (x, y) position.
+ *
+ * Returns:
+ * {pickPosition, pickNormal, pickObject}
+ */
+x3dom.Runtime.prototype.shootRay = function(x, y) {
+ var doc = this.canvas.doc;
+ var info = doc._viewarea._pickingInfo;
+
+ doc.onPick(this.canvas.gl, x, y);
+
+ return {
+ pickPosition: info.pickObj ? info.pickPos : null,
+ pickNormal: info.pickObj ? info.pickNorm : null,
+ pickObject: info.pickObj ? info.pickObj._xmlNode : null
+ };
+};
+
+/**
+ * Function: getWidth
+ *
+ * Returns the width of the canvas element.
+ */
+x3dom.Runtime.prototype.getWidth = function() {
+ return this.canvas.doc._viewarea._width;
+};
+
+/**
+ * Function: getHeight
+ *
+ * Returns the width of the canvas element.
+ */
+x3dom.Runtime.prototype.getHeight = function() {
+ return this.canvas.doc._viewarea._height;
+};
+
+/**
+ * Function: mousePosition
+ *
+ * Returns the 2d canvas layer position [x, y] for a given mouse event, i.e.,
+ * the mouse cursor's x and y positions relative to the canvas (x3d) element.
+ */
+x3dom.Runtime.prototype.mousePosition = function(event) {
+ var pos = this.canvas.mousePosition(event);
+
+ return [pos.x, pos.y];
+};
+
+/**
+ * Function: calcCanvasPos
+ *
+ * Returns the 2d screen position [cx, cy] for a given point [wx, wy, wz] in world coordinates.
+ */
+x3dom.Runtime.prototype.calcCanvasPos = function(wx, wy, wz) {
+ var pnt = new x3dom.fields.SFVec3f(wx, wy, wz);
+
+ var mat = this.canvas.doc._viewarea.getWCtoCCMatrix();
+ var pos = mat.multFullMatrixPnt(pnt);
+
+ var w = this.canvas.doc._viewarea._width;
+ var h = this.canvas.doc._viewarea._height;
+
+ var x = Math.round((pos.x + 1) * (w - 1) / 2);
+ var y = Math.round((h - 1) * (1 - pos.y) / 2);
+
+ return [x, y];
+};
+
+/**
+ * Function: calcPagePos
+ *
+ * Returns the 2d page (returns the mouse coordinates relative to the document) position [cx, cy]
+ * for a given point [wx, wy, wz] in world coordinates.
+ */
+x3dom.Runtime.prototype.calcPagePos = function(wx, wy, wz) {
+ var elem = this.canvas.canvas.offsetParent;
+
+ if (!elem) {
+ x3dom.debug.logError("Can't calc page pos without offsetParent.");
+ return [0, 0];
+ }
+
+ var canvasPos = elem.getBoundingClientRect();
+ var mousePos = this.calcCanvasPos(wx, wy, wz);
+
+ var scrollLeft = window.pageXOffset || document.body.scrollLeft;
+ var scrollTop = window.pageYOffset || document.body.scrollTop;
+
+ var compStyle = document.defaultView.getComputedStyle(elem, null);
+
+ var paddingLeft = parseFloat(compStyle.getPropertyValue('padding-left'));
+ var borderLeftWidth = parseFloat(compStyle.getPropertyValue('border-left-width'));
+
+ var paddingTop = parseFloat(compStyle.getPropertyValue('padding-top'));
+ var borderTopWidth = parseFloat(compStyle.getPropertyValue('border-top-width'));
+
+ var x = canvasPos.left + paddingLeft + borderLeftWidth + scrollLeft + mousePos[0];
+ var y = canvasPos.top + paddingTop + borderTopWidth + scrollTop + mousePos[1];
+
+ return [x, y];
+};
+
+/**
+ * Function: calcClientPos
+ *
+ * Returns the 2d client (returns the mouse coordinates relative to the window) position [cx, cy]
+ * for a given point [wx, wy, wz] in world coordinates.
+ */
+x3dom.Runtime.prototype.calcClientPos = function(wx, wy, wz) {
+ var elem = this.canvas.canvas.offsetParent;
+
+ if (!elem) {
+ x3dom.debug.logError("Can't calc client pos without offsetParent.");
+ return [0, 0];
+ }
+
+ var canvasPos = elem.getBoundingClientRect();
+ var mousePos = this.calcCanvasPos(wx, wy, wz);
+
+ var compStyle = document.defaultView.getComputedStyle(elem, null);
+
+ var paddingLeft = parseFloat(compStyle.getPropertyValue('padding-left'));
+ var borderLeftWidth = parseFloat(compStyle.getPropertyValue('border-left-width'));
+
+ var paddingTop = parseFloat(compStyle.getPropertyValue('padding-top'));
+ var borderTopWidth = parseFloat(compStyle.getPropertyValue('border-top-width'));
+
+ var x = canvasPos.left + paddingLeft + borderLeftWidth + mousePos[0];
+ var y = canvasPos.top + paddingTop + borderTopWidth + mousePos[1];
+
+ return [x, y];
+};
+
+/**
+ * Function: getScreenshot
+ *
+ * Returns a Base64 encoded png image consisting of the current rendering.
+ *
+ * Returns:
+ * The Base64 encoded PNG image string
+ */
+x3dom.Runtime.prototype.getScreenshot = function() {
+ var url = "";
+ var backend = this.canvas.backend;
+ var canvas = this.canvas.canvas;
+
+ if(canvas) {
+ if(backend == "flash") {
+ url = canvas.getScreenshot();
+ }
+ else {
+ // first flip along y axis
+ var canvas2d = document.createElement("canvas");
+ canvas2d.width = canvas.width;
+ canvas2d.height = canvas.height;
+
+ var ctx = canvas2d.getContext("2d");
+ ctx.drawImage(canvas, 0, 0, canvas.width, canvas.height);
+ ctx.scale(1, -1);
+ ctx.translate(0, -canvas.height);
+
+ url = canvas2d.toDataURL();
+ }
+ }
+
+ return url;
+};
+
+/**
+ * Function: getCanvas
+ *
+ * Returns the internal canvas element (only valid for WebGL backend)
+ *
+ * Returns:
+ * The internal canvas element
+ */
+x3dom.Runtime.prototype.getCanvas = function() {
+ return this.canvas.canvas;
+};
+
+/**
+ * Function: lightMatrix
+ *
+ * Returns the current light matrix.
+ *
+ * Returns:
+ * The light matrix
+ */
+x3dom.Runtime.prototype.lightMatrix = function() {
+ this.canvas.doc._viewarea.getLightMatrix();
+};
+
+/**
+ * APIFunction: resetView
+ *
+ * Resets the view to initial.
+ *
+ */
+x3dom.Runtime.prototype.resetView = function() {
+ this.canvas.doc._viewarea.resetView();
+};
+
+/**
+ * Function: lightView
+ *
+ * Navigates to the first light, if any.
+ *
+ * Returns:
+ * True if navigation was possible, false otherwise.
+ */
+x3dom.Runtime.prototype.lightView = function() {
+ if (this.canvas.doc._nodeBag.lights.length > 0) {
+ this.canvas.doc._viewarea.animateTo(this.canvas.doc._viewarea.getLightMatrix()[0],
+ this.canvas.doc._scene.getViewpoint());
+ return true;
+ } else {
+ x3dom.debug.logInfo("No lights to navigate to.");
+ return false;
+ }
+};
+
+/**
+ * APIFunction: uprightView
+ *
+ * Navigates to upright view
+ *
+ */
+x3dom.Runtime.prototype.uprightView = function() {
+ this.canvas.doc._viewarea.uprightView();
+};
+
+/**
+ * APIFunction: fitAll
+ *
+ * Zooms so that all objects are fully visible. Without change the actual Viewpoint orientation
+ *
+ * Parameter:
+ * updateCenterOfRotation - a boolean value that specifies if the new center of rotation is set
+ *
+ */
+x3dom.Runtime.prototype.fitAll = function(updateCenterOfRotation)
+{
+ if (updateCenterOfRotation === undefined) {
+ updateCenterOfRotation = true;
+ }
+
+ var scene = this.canvas.doc._scene;
+ scene.updateVolume();
+
+ this.canvas.doc._viewarea.fit(scene._lastMin, scene._lastMax, updateCenterOfRotation);
+};
+
+/**
+ * APIFunction: fitObject
+ *
+ * Zooms so that a given object are fully visible. Without change the actual Viewpoint orientation
+ *
+ * Parameter:
+ * updateCenterOfRotation - a boolean value that specifies if the new center of rotation is set
+ *
+ */
+x3dom.Runtime.prototype.fitObject = function(obj, updateCenterOfRotation)
+{
+ if (obj && obj._x3domNode)
+ {
+ if (updateCenterOfRotation === undefined) {
+ updateCenterOfRotation = true;
+ }
+
+ var min = x3dom.fields.SFVec3f.MAX();
+ var max = x3dom.fields.SFVec3f.MIN();
+
+ var vol = obj._x3domNode.getVolume();
+ vol.getBounds(min, max);
+
+ var mat = obj._x3domNode.getCurrentTransform();
+
+ min = mat.multMatrixPnt(min);
+ max = mat.multMatrixPnt(max);
+
+ //TODO: revise separation of "getVolume" and "getCurrentTransform"
+ // for the transform nodes - currently, both "overlap" because
+ // both include the transform's own matrix
+ // but which is what you usually expect from both methods...
+ if (x3dom.isa(obj._x3domNode, x3dom.nodeTypes.X3DTransformNode))
+ {
+ var invMat = obj._x3domNode._trafo.inverse();
+ min = invMat.multMatrixPnt(min);
+ max = invMat.multMatrixPnt(max);
+ }
+
+ this.canvas.doc._viewarea.fit(min, max, updateCenterOfRotation);
+ }
+};
+
+/**
+ * APIFunction: showAll
+ *
+ * Zooms so that all objects are fully visible.
+ *
+ * Parameter:
+ * axis - the axis as string: posX, negX, posY, negY, posZ, negZ
+ *
+ */
+x3dom.Runtime.prototype.showAll = function(axis) {
+ this.canvas.doc._viewarea.showAll(axis);
+};
+
+/**
+ * APIFunction: showObject
+ *
+ * Zooms so that a given object is fully visible in the middle of the screen.
+ *
+ * Parameter:
+ * obj - the scene-graph element on which to focus
+ */
+x3dom.Runtime.prototype.showObject = function(obj)
+{
+ if (obj && obj._x3domNode)
+ {
+ var min = x3dom.fields.SFVec3f.MAX();
+ var max = x3dom.fields.SFVec3f.MIN();
+
+ var vol = obj._x3domNode.getVolume();
+ vol.getBounds(min, max);
+
+ var mat = obj._x3domNode.getCurrentTransform();
+
+ min = mat.multMatrixPnt(min);
+ max = mat.multMatrixPnt(max);
+
+ var viewarea = this.canvas.doc._viewarea;
+
+ // assume FOV_smaller as camera's fovMode
+ var focalLen = (viewarea._width < viewarea._height) ?
+ viewarea._width : viewarea._height;
+
+ var n0 = new x3dom.fields.SFVec3f(0, 0, 1); // facingDir
+ var viewpoint = this.canvas.doc._scene.getViewpoint();
+ var fov = viewpoint.getFieldOfView() / 2.0;
+ var ta = Math.tan(fov);
+
+ if (Math.abs(ta) > x3dom.fields.Eps) {
+ focalLen /= ta;
+ }
+
+ var w = viewarea._width - 1;
+ var h = viewarea._height - 1;
+
+ var frame = 0.25;
+ var minScreenPos = new x3dom.fields.SFVec2f(frame * w, frame * h);
+
+ frame = 0.75;
+ var maxScreenPos = new x3dom.fields.SFVec2f(frame * w, frame * h);
+
+ var dia2 = max.subtract(min).multiply(0.5); // half diameter
+ var rw = dia2.length(); // approx radius
+
+ var pc = min.add(dia2); // center in wc
+ var vc = maxScreenPos.subtract(minScreenPos).multiply(0.5);
+
+ var rs = 1.5 * vc.length();
+ vc = vc.add(minScreenPos);
+
+ var dist = 1.0;
+ if (rs > x3dom.fields.Eps) {
+ dist = (rw / rs) * Math.sqrt(vc.x*vc.x + vc.y*vc.y + focalLen*focalLen);
+ }
+
+ n0 = mat.multMatrixVec(n0).normalize();
+ n0 = n0.multiply(dist);
+ var p0 = pc.add(n0);
+
+ var qDir = x3dom.fields.Quaternion.rotateFromTo(new x3dom.fields.SFVec3f(0, 0, 1), n0);
+ var R = qDir.toMatrix();
+
+ var T = x3dom.fields.SFMatrix4f.translation(p0.negate());
+ var M = x3dom.fields.SFMatrix4f.translation(p0);
+
+ M = M.mult(R).mult(T).mult(M);
+ var viewmat = M.inverse();
+
+ viewarea.animateTo(viewmat, viewpoint);
+ }
+};
+
+/**
+ * APIMethod getCenter
+ *
+ * Returns the center of a X3DShapeNode or X3DGeometryNode.
+ *
+ * Parameters:
+ * domNode: the node for which its center shall be returned
+ *
+ * Returns:
+ * Node center (or null if no Shape or Geometry)
+ */
+x3dom.Runtime.prototype.getCenter = function(domNode) {
+ if (domNode && domNode._x3domNode &&
+ (this.isA(domNode, "X3DShapeNode") || this.isA(domNode, "X3DGeometryNode")))
+ {
+ return domNode._x3domNode.getCenter();
+ }
+
+ return null;
+};
+
+/**
+ * APIMethod getCurrentTransform
+ *
+ * Returns the current to world transformation of a node.
+ *
+ * Parameters:
+ * domNode: the node for which its transformation shall be returned
+ *
+ * Returns:
+ * Transformation matrix (or null no valid node is given)
+ */
+x3dom.Runtime.prototype.getCurrentTransform = function(domNode) {
+ if (domNode && domNode._x3domNode)
+ {
+ return domNode._x3domNode.getCurrentTransform();
+ }
+
+ return null;
+};
+
+/**
+ * APIMethod getBBox
+ *
+ * Returns the bounding box of a node.
+ *
+ * Parameters:
+ * domNode: the node for which its volume shall be returned
+ *
+ * Returns:
+ * The min and max positions of the node's bounding box.
+ */
+x3dom.Runtime.prototype.getBBox = function(domNode) {
+ if (domNode && domNode._x3domNode && this.isA(domNode, "X3DBoundedObject"))
+ {
+ var vol = domNode._x3domNode.getVolume();
+
+ return {
+ min: x3dom.fields.SFVec3f.copy(vol.min),
+ max: x3dom.fields.SFVec3f.copy(vol.max)
+ }
+ }
+
+ return null;
+};
+
+/**
+ * APIMethod getSceneBBox
+ *
+ * Returns the bounding box of the scene.
+ *
+ * Returns:
+ * The min and max positions of the scene's bounding box.
+ */
+x3dom.Runtime.prototype.getSceneBBox = function() {
+ var scene = this.canvas.doc._scene;
+ scene.updateVolume();
+
+ return {
+ min: x3dom.fields.SFVec3f.copy(scene._lastMin),
+ max: x3dom.fields.SFVec3f.copy(scene._lastMax)
+ }
+};
+
+/**
+ * APIFunction: debug
+ *
+ * Displays or hides the debug window. If parameter is omitted,
+ * the current visibility status is returned.
+ *
+ * Parameter:
+ * show - true to show debug window, false to hide
+ *
+ * Returns:
+ * Current visibility status of debug window (true=visible, false=hidden)
+ */
+x3dom.Runtime.prototype.debug = function(show) {
+ var doc = this.canvas.doc;
+ if (doc._viewarea._visDbgBuf === undefined)
+ doc._viewarea._visDbgBuf = (doc._x3dElem.getAttribute("showLog") === 'true');
+
+ if (arguments.length > 0) {
+ if (show === true) {
+ doc._viewarea._visDbgBuf = true;
+ x3dom.debug.logContainer.style.display = "block";
+ }
+ else {
+ doc._viewarea._visDbgBuf = false;
+ x3dom.debug.logContainer.style.display = "none";
+ }
+ }
+ else {
+ doc._viewarea._visDbgBuf = !doc._viewarea._visDbgBuf;
+ x3dom.debug.logContainer.style.display = (doc._viewarea._visDbgBuf == true) ? "block" : "none";
+ }
+ doc.needRender = true;
+
+ return doc._viewarea._visDbgBuf;
+};
+
+/**
+ * APIFunction: navigationType
+ *
+ * Readout of the currently active navigation.
+ *
+ * Returns:
+ * A string representing the active navigation type
+ */
+x3dom.Runtime.prototype.navigationType = function() {
+ return this.canvas.doc._scene.getNavigationInfo().getType();
+};
+
+/**
+ * APIFunction: noNav
+ *
+ * Switches to noNav mode
+ */
+x3dom.Runtime.prototype.noNav = function() {
+ this.canvas.doc._scene.getNavigationInfo().setType("none");
+};
+
+/**
+ * APIFunction: examine
+ *
+ * Switches to examine mode
+ */
+x3dom.Runtime.prototype.examine = function() {
+ this.canvas.doc._scene.getNavigationInfo().setType("examine");
+};
+
+/**
+ * APIFunction: turnTable
+ *
+ * Switches to turnTable mode
+ */
+x3dom.Runtime.prototype.turnTable = function() {
+ this.canvas.doc._scene.getNavigationInfo().setType("turntable");
+};
+
+/**
+ * APIFunction: fly
+ *
+ * Switches to fly mode
+ */
+x3dom.Runtime.prototype.fly = function() {
+ this.canvas.doc._scene.getNavigationInfo().setType("fly");
+};
+
+/**
+ * APIFunction: freeFly
+ *
+ * Switches to freeFly mode
+ */
+x3dom.Runtime.prototype.freeFly = function() {
+ this.canvas.doc._scene.getNavigationInfo().setType("freefly");
+};
+
+/**
+ * APIFunction: lookAt
+ *
+ * Switches to lookAt mode
+ */
+x3dom.Runtime.prototype.lookAt = function() {
+ this.canvas.doc._scene.getNavigationInfo().setType("lookat");
+};
+/**
+ * APIFunction: lookAround
+ *
+ * Switches to lookAround mode
+ */
+x3dom.Runtime.prototype.lookAround = function() {
+ this.canvas.doc._scene.getNavigationInfo().setType("lookaround");
+};
+
+/**
+ * APIFunction: walk
+ *
+ * Switches to walk mode
+ */
+x3dom.Runtime.prototype.walk = function() {
+ this.canvas.doc._scene.getNavigationInfo().setType("walk");
+};
+
+/**
+ * APIFunction: game
+ *
+ * Switches to game mode
+ */
+x3dom.Runtime.prototype.game = function() {
+ this.canvas.doc._scene.getNavigationInfo().setType("game");
+};
+
+/**
+ * APIFunction: helicopter
+ *
+ * Switches to helicopter mode
+ */
+x3dom.Runtime.prototype.helicopter = function() {
+ this.canvas.doc._scene.getNavigationInfo().setType("helicopter");
+};
+
+/**
+ * Function: resetExamin
+ *
+ * Resets all variables required by examine mode to init state
+ */
+ x3dom.Runtime.prototype.resetExamin = function() {
+ var viewarea = this.canvas.doc._viewarea;
+ viewarea._rotMat = x3dom.fields.SFMatrix4f.identity();
+ viewarea._transMat = x3dom.fields.SFMatrix4f.identity();
+ viewarea._movement = new x3dom.fields.SFVec3f(0, 0, 0);
+ viewarea._needNavigationMatrixUpdate = true;
+ this.canvas.doc.needRender = true;
+ };
+
+/**
+ * Function: togglePoints
+ *
+ * Toggles points attribute
+ */
+x3dom.Runtime.prototype.togglePoints = function(lines) {
+ var doc = this.canvas.doc;
+ var mod = (lines === true) ? 3 : 2;
+
+ doc._viewarea._points = ++doc._viewarea._points % mod;
+ doc.needRender = true;
+
+ return doc._viewarea._points;
+};
+
+/**
+ * Function: pickRect
+ *
+ * Returns an array of all shape elements that are within the picked rectangle
+ * defined by (x1, y1) and (x2, y2) in canvas coordinates
+ */
+x3dom.Runtime.prototype.pickRect = function(x1, y1, x2, y2) {
+ return this.canvas.doc.onPickRect(this.canvas.gl, x1, y1, x2, y2);
+};
+
+/**
+ * Function: pickMode
+ *
+ * Get the current pickMode intersect type
+ *
+ * Parameters:
+ * internal - true/false. If given return the internal representation.
+ * Only use for debugging.
+ *
+ * Returns:
+ * The current intersect type value suitable to use with changePickMode
+ * If parameter is, given, provide with internal representation.
+ */
+x3dom.Runtime.prototype.pickMode = function(options) {
+ if (options && options.internal === true) {
+ return this.canvas.doc._scene._vf.pickMode;
+ }
+ return this.canvas.doc._scene._vf.pickMode.toLowerCase();
+};
+
+/**
+ * Function: changePickMode
+ *
+ * Alter the value of intersect type. Can be one of: box, idBuf, idBuf24, idBufId, color, texCoord.
+ * Other values are ignored.
+ *
+ * Parameters:
+ * type - The new intersect type: box, idBuf, idBuf24, idBufId, color, texCoord
+ *
+ * Returns:
+ * true if the type has been changed, false otherwise
+ */
+x3dom.Runtime.prototype.changePickMode = function(type) {
+ // pick type one of : box, idBuf, idBuf24, idBufId, color, texCoord
+ type = type.toLowerCase();
+
+ switch(type) {
+ case 'idbuf': type = 'idBuf'; break;
+ case 'idbuf24': type = 'idBuf24'; break;
+ case 'idbufid': type = 'idBufId'; break;
+ case 'texcoord': type = 'texCoord'; break;
+ case 'color': type = 'color'; break;
+ case 'box': type = 'box'; break;
+ default:
+ x3dom.debug.logWarning("Switch pickMode to "+ type + ' unknown intersect type');
+ type = undefined;
+ }
+
+ if (type !== undefined) {
+ this.canvas.doc._scene._vf.pickMode = type;
+ x3dom.debug.logInfo("Switched pickMode to '" + type + "'.");
+ return true;
+ }
+
+ return false;
+};
+
+/**
+ * APIFunction: speed
+ *
+ * Get the current speed value. If parameter is given the new speed value is set.
+ *
+ * Parameters:
+ * newSpeed - The new speed value (optional)
+ *
+ * Returns:
+ * The current speed value
+ */
+x3dom.Runtime.prototype.speed = function(newSpeed) {
+ var navi = this.canvas.doc._scene.getNavigationInfo();
+ if (newSpeed) {
+ navi._vf.speed = newSpeed;
+ x3dom.debug.logInfo("Changed navigation speed to " + navi._vf.speed);
+ }
+ return navi._vf.speed;
+};
+
+/**
+ * APIFunction: statistics
+ *
+ * Get or set statistics info. If parameter is omitted, this method
+ * only returns the the visibility status of the statistics info overlay.
+ *
+ * Parameters:
+ * mode - true or false. To enable or disable the statistics info
+ *
+ * Returns:
+ * The current visibility of the statistics info (true = visible, false = invisible)
+ */
+x3dom.Runtime.prototype.statistics = function(mode) {
+ var states = this.canvas.stateViewer;
+ if (states) {
+ this.canvas.doc.needRender = true;
+ if (mode === true) {
+ states.display(mode);
+ return true;
+ }
+ else if (mode === false) {
+ states.display(mode);
+ return false;
+ }
+ else {
+ states.display(!states.active);
+ // if no parameter is given return current status (false = not visible, true = visible)
+ return states.active;
+ }
+ }
+ return false;
+};
+
+/**
+ * Function: processIndicator
+ *
+ * Enable or disable the process indicator. If parameter is omitted, this method
+ * only returns the the visibility status of the progress bar overlay.
+ *
+ * Parameters:
+ * mode - true or false. To enable or disable the progress indicator
+ *
+ * Returns:
+ * The current visibility of the progress indicator info (true = visible, false = invisible)
+ */
+x3dom.Runtime.prototype.processIndicator = function(mode) {
+ var processDiv = this.canvas.progressDiv;
+ if (processDiv) {
+ if (mode === true) {
+ processDiv.style.display = 'inline';
+ return true;
+ }
+ else if (mode === false) {
+ processDiv.style.display = 'none';
+ return false;
+ }
+
+ // if no parameter is given return current status (false = not visible, true = visible)
+ return processDiv.style.display != 'none'
+ }
+ return false;
+};
+
+/** Get properties */
+x3dom.Runtime.prototype.properties = function() {
+ return this.canvas.doc.properties;
+};
+
+/** Get current backend name */
+x3dom.Runtime.prototype.backendName = function() {
+ return this.canvas.backend;
+};
+
+/** Get current framerate */
+x3dom.Runtime.prototype.getFPS = function() {
+ return this.fps;
+};
+
+
+/**
+ * APIMethod isA
+ *
+ * Test a DOM node object against a node type string. This method
+ * can be used to determine the "type" of a DOM node.
+ *
+ * Parameters:
+ * domNode: the node to test for
+ * nodeType: node name to test domNode against
+ *
+ * Returns:
+ * True or false
+ */
+x3dom.Runtime.prototype.isA = function(domNode, nodeType) {
+ var inherits = false;
+
+ if (nodeType && domNode && domNode._x3domNode) {
+ if (nodeType === "") {
+ nodeType = "X3DNode";
+ }
+ inherits = x3dom.isa(domNode._x3domNode,
+ x3dom.nodeTypesLC[nodeType.toLowerCase()]);
+ }
+
+ return inherits;
+};
+
+/*
+ * X3DOM JavaScript Library
+ * http://www.x3dom.org
+ *
+ * (C)2009 Fraunhofer IGD, Darmstadt, Germany
+ * Dual licensed under the MIT and GPL
+ *
+ * Based on code originally provided by
+ * Philip Taylor: http://philip.html5.org
+ */
+
+x3dom.detectActiveX = function() {
+ var isInstalled = false;
+
+ if (window.ActiveXObject) {
+ var control = null;
+
+ try {
+ control = new ActiveXObject('AVALONATX.InstantPluginATXCtrl.1');
+ } catch (e) {
+ }
+
+ if (control) {
+ isInstalled = true;
+ }
+ }
+
+ return isInstalled;
+};
+
+x3dom.rerouteSetAttribute = function(node, browser) {
+ // save old setAttribute method
+ node._setAttribute = node.setAttribute;
+ node.setAttribute = function(name, value) {
+ var id = node.getAttribute("_x3domNode");
+ var anode = browser.findNode(id);
+
+ if (anode)
+ return anode.parseField(name, value);
+ else
+ return 0;
+ };
+
+ for(var i=0; i < node.childNodes.length; i++) {
+ var child = node.childNodes[i];
+ x3dom.rerouteSetAttribute(child, browser);
+ }
+};
+
+x3dom.insertActiveX = function(x3d) {
+
+ if (typeof x3dom.atxCtrlCounter == 'undefined') {
+ x3dom.atxCtrlCounter = 0;
+ }
+
+ var height = x3d.getAttribute("height");
+ var width = x3d.getAttribute("width");
+
+ var parent = x3d.parentNode;
+
+ var divelem = document.createElement("div");
+ divelem.setAttribute("id", "x3dplaceholder");
+
+ var inserted = parent.insertBefore(divelem, x3d);
+
+ // hide x3d div
+ var hiddenx3d = document.createElement("div");
+ hiddenx3d.style.display = "none";
+ parent.appendChild(hiddenx3d);
+ parent.removeChild(x3d);
+ hiddenx3d.appendChild(x3d);
+
+ var atx = document.createElement("object");
+
+ var containerName = "Avalon" + x3dom.atxCtrlCounter;
+ x3dom.atxCtrlCounter++;
+
+ atx.setAttribute("id", containerName);
+ atx.setAttribute("classid", "CLSID:F3254BA0-99FF-4D14-BD81-EDA9873A471E");
+ atx.setAttribute("width", width ? width : "500");
+ atx.setAttribute("height", height ? height : "500");
+
+ inserted.appendChild(atx);
+
+ var atxctrl = document.getElementById(containerName);
+ var browser = atxctrl.getBrowser();
+ var scene = browser.importDocument(x3d);
+ browser.replaceWorld(scene);
+
+ // add backtrack method to get browser from x3d node instead of the ctrl
+ x3d.getBrowser = function() {
+ return atxctrl.getBrowser();
+ };
+
+ x3dom.rerouteSetAttribute(x3d, browser);
+};
+
+// holds the UserAgent feature
+x3dom.userAgentFeature = {
+ supportsDOMAttrModified: false
+};
+
+
+(function loadX3DOM() {
+ "use strict";
+
+ var onload = function() {
+ var i,j; // counters
+
+ // Search all X3D elements in the page
+ var x3ds_unfiltered = document.getElementsByTagName('X3D');
+ var x3ds = [];
+
+ // check if element already has been processed
+ for (i=0; i < x3ds_unfiltered.length; i++) {
+ if (x3ds_unfiltered[i].hasRuntime === undefined)
+ x3ds.push(x3ds_unfiltered[i]);
+ }
+
+ // ~~ Components and params {{{ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ var params;
+ var settings = new x3dom.Properties(); // stores the stuff in <param>
+ var validParams = array_to_object([
+ 'showLog',
+ 'showStat',
+ 'showProgress',
+ 'PrimitiveQuality',
+ 'components',
+ 'loadpath',
+ 'disableDoubleClick',
+ 'backend',
+ 'altImg',
+ 'flashrenderer',
+ 'swfpath',
+ 'runtimeEnabled',
+ 'keysEnabled',
+ 'showTouchpoints',
+ 'disableTouch',
+ 'maxActiveDownloads'
+ ]);
+ var components, prefix;
+ var showLoggingConsole = false;
+
+ // for each X3D element
+ for (i=0; i < x3ds.length; i++) {
+
+ // default parameters
+ settings.setProperty("showLog", x3ds[i].getAttribute("showLog") || 'false');
+ settings.setProperty("showStat", x3ds[i].getAttribute("showStat") || 'false');
+ settings.setProperty("showProgress", x3ds[i].getAttribute("showProgress") || 'true');
+ settings.setProperty("PrimitiveQuality", x3ds[i].getAttribute("PrimitiveQuality") || 'High');
+
+ // for each param element inside the X3D element
+ // add settings to properties object
+ params = x3ds[i].getElementsByTagName('PARAM');
+ for (j=0; j < params.length; j++) {
+ if (params[j].getAttribute('name') in validParams) {
+ settings.setProperty(params[j].getAttribute('name'), params[j].getAttribute('value'));
+ } else {
+ //x3dom.debug.logError("Unknown parameter: " + params[j].getAttribute('name'));
+ }
+ }
+
+ // enable log
+ if (settings.getProperty('showLog') === 'true') {
+ showLoggingConsole = true;
+ }
+
+ if (typeof X3DOM_SECURITY_OFF != 'undefined' && X3DOM_SECURITY_OFF === true) {
+ // load components from params or default to x3d attribute
+ components = settings.getProperty('components', x3ds[i].getAttribute("components"));
+ if (components) {
+ prefix = settings.getProperty('loadpath', x3ds[i].getAttribute("loadpath"));
+ components = components.trim().split(',');
+ for (j=0; j < components.length; j++) {
+ x3dom.loadJS(components[j] + ".js", prefix);
+ }
+ }
+
+ // src=foo.x3d adding inline node, not a good idea, but...
+ if (x3ds[i].getAttribute("src")) {
+ var _scene = document.createElement("scene");
+ var _inl = document.createElement("Inline");
+ _inl.setAttribute("url", x3ds[i].getAttribute("src"));
+ _scene.appendChild(_inl);
+ x3ds[i].appendChild(_scene);
+ }
+ }
+ }
+ // }}}
+
+ if (showLoggingConsole == true) {
+ x3dom.debug.activate(true);
+ } else {
+ x3dom.debug.activate(false);
+ }
+
+ // Convert the collection into a simple array (is this necessary?)
+ x3ds = Array.map(x3ds, function (n) {
+ n.hasRuntime = true;
+ return n;
+ });
+
+ var w3sg = document.getElementsByTagName('webSG'); // THINKABOUTME: shall we still support exp. WebSG?!
+
+ for (i=0; i<w3sg.length; i++) {
+ w3sg[i].hasRuntime = false;
+ x3ds.push(w3sg[i]);
+ }
+
+ if (x3dom.versionInfo !== undefined) {
+ x3dom.debug.logInfo("X3DOM version " + x3dom.versionInfo.version + ", " +
+ "Revison <a href='https://github.com/x3dom/x3dom/tree/"+ x3dom.versionInfo.revision +"'>"
+ + x3dom.versionInfo.revision + "</a>, " +
+ "Date " + x3dom.versionInfo.date);
+ }
+
+ x3dom.debug.logInfo("Found " + (x3ds.length - w3sg.length) + " X3D and " +
+ w3sg.length + " (experimental) WebSG nodes...");
+
+ // Create a HTML canvas for every X3D scene and wrap it with
+ // an X3D canvas and load the content
+ var x3d_element;
+ var x3dcanvas;
+ var altDiv, altP, aLnk, altImg;
+ var t0, t1;
+
+ for (i=0; i < x3ds.length; i++)
+ {
+ x3d_element = x3ds[i];
+
+ // http://www.howtocreate.co.uk/wrongWithIE/?chapter=navigator.plugins
+ if (x3dom.detectActiveX()) {
+ x3dom.insertActiveX(x3d_element);
+ continue;
+ }
+
+ x3dcanvas = new x3dom.X3DCanvas(x3d_element, x3dom.canvases.length);
+
+ x3dom.canvases.push(x3dcanvas);
+
+ if (x3dcanvas.gl === null) {
+
+ altDiv = document.createElement("div");
+ altDiv.setAttribute("class", "x3dom-nox3d");
+ altDiv.setAttribute("id", "x3dom-nox3d");
+
+ altP = document.createElement("p");
+ altP.appendChild(document.createTextNode("WebGL is not yet supported in your browser. "));
+ aLnk = document.createElement("a");
+ aLnk.setAttribute("href","http://www.x3dom.org/?page_id=9");
+ aLnk.appendChild(document.createTextNode("Follow link for a list of supported browsers... "));
+
+ altDiv.appendChild(altP);
+ altDiv.appendChild(aLnk);
+
+ x3dcanvas.x3dElem.appendChild(altDiv);
+
+ // remove the stats div (it's not added when WebGL doesn't work)
+ if (x3dcanvas.stateViewer) {
+ x3d_element.removeChild(x3dcanvas.stateViewer.viewer);
+ }
+
+ continue;
+ }
+
+ t0 = new Date().getTime();
+
+ x3ds[i].runtime = new x3dom.Runtime(x3ds[i], x3dcanvas);
+ x3ds[i].runtime.initialize(x3ds[i], x3dcanvas);
+
+ if (x3dom.runtime.ready) {
+ x3ds[i].runtime.ready = x3dom.runtime.ready;
+ }
+
+ // no backend found method system wide call
+ if (x3dcanvas.backend == '') {
+ x3dom.runtime.noBackendFound();
+ }
+
+ x3dcanvas.load(x3ds[i], i, settings);
+
+ // show or hide statistics based on param/x3d attribute settings
+ if (settings.getProperty('showStat') === 'true') {
+ x3ds[i].runtime.statistics(true);
+ } else {
+ x3ds[i].runtime.statistics(false);
+ }
+
+ if (settings.getProperty('showProgress') === 'true') {
+ if (settings.getProperty('showProgress') === 'bar'){
+ x3dcanvas.progressDiv.setAttribute("class", "x3dom-progress bar");
+ }
+ x3ds[i].runtime.processIndicator(true);
+ } else {
+ x3ds[i].runtime.processIndicator(false);
+ }
+
+ t1 = new Date().getTime() - t0;
+ x3dom.debug.logInfo("Time for setup and init of GL element no. " + i + ": " + t1 + " ms.");
+ }
+
+ var ready = (function(eventType) {
+ var evt = null;
+
+ if (document.createEvent) {
+ evt = document.createEvent("Events");
+ evt.initEvent(eventType, true, true);
+ document.dispatchEvent(evt);
+ } else if (document.createEventObject) {
+ evt = document.createEventObject();
+ // http://stackoverflow.com/questions/1874866/how-to-fire-onload-event-on-document-in-ie
+ document.body.fireEvent('on' + eventType, evt);
+ }
+ })('load');
+ };
+
+ var onunload = function() {
+ if (x3dom.canvases) {
+ for (var i=0; i<x3dom.canvases.length; i++) {
+ x3dom.canvases[i].doc.shutdown(x3dom.canvases[i].gl);
+ }
+ x3dom.canvases = [];
+ }
+ };
+
+ /** Initializes an <x3d> root element that was added after document load. */
+ x3dom.reload = function() {
+ onload();
+ };
+
+ /* FIX PROBLEM IN CHROME - HACK - searching for better solution !!! */
+ if (navigator.userAgent.indexOf("Chrome") != -1) {
+ document.__getElementsByTagName = document.getElementsByTagName;
+
+ document.getElementsByTagName = function(tag) {
+ var obj = [];
+ var elems = this.__getElementsByTagName("*");
+
+ if(tag =="*"){
+ obj = elems;
+ } else {
+ tag = tag.toUpperCase();
+ for (var i = 0; i < elems.length; i++) {
+ var tagName = elems[i].tagName.toUpperCase();
+ if (tagName === tag) {
+ obj.push(elems[i]);
+ }
+ }
+ }
+
+ return obj;
+ };
+
+ document.__getElementById = document.getElementById;
+ document.getElementById = function(id) {
+ var obj = this.__getElementById(id);
+
+ if (!obj) {
+ var elems = this.__getElementsByTagName("*");
+ for (var i=0; i<elems.length && !obj; i++) {
+ if (elems[i].getAttribute("id") === id) {
+ obj = elems[i];
+ }
+ }
+ }
+ return obj;
+ };
+
+ } else { /* END OF HACK */
+ document.__getElementById = document.getElementById;
+ document.getElementById = function(id) {
+ var obj = this.__getElementById(id);
+
+ if (!obj) {
+ var elems = this.getElementsByTagName("*");
+ for (var i=0; i<elems.length && !obj; i++) {
+ if (elems[i].getAttribute("id") === id) {
+ obj = elems[i];
+ }
+ }
+ }
+ return obj;
+ };
+ }
+
+ if (window.addEventListener) {
+ window.addEventListener('load', onload, false);
+ window.addEventListener('unload', onunload, false);
+ window.addEventListener('reload', onunload, false);
+ } else if (window.attachEvent) {
+ window.attachEvent('onload', onload);
+ window.attachEvent('onunload', onunload);
+ window.attachEvent('onreload', onunload);
+ }
+
+ // Initialize if we were loaded after 'DOMContentLoaded' already fired.
+ // This can happen if the script was loaded by other means.
+ if (document.readyState === "complete") {
+ window.setTimeout( function() { onload(); }, 20 );
+ }
+})();
+
+/*
+ * X3DOM JavaScript Library
+ * http://www.x3dom.org
+ *
+ * (C)2009 Fraunhofer IGD, Darmstadt, Germany
+ * Dual licensed under the MIT and GPL
+ *
+ * Based on code originally provided by
+ * Philip Taylor: http://philip.html5.org
+ */
+
+x3dom.Cache = function () {
+ this.textures = [];
+ this.shaders = [];
+};
+
+/**
+ * Returns a Texture 2D
+ */
+x3dom.Cache.prototype.getTexture2D = function (gl, doc, url, bgnd, crossOrigin, scale, genMipMaps) {
+ var textureIdentifier = url;
+
+ if (this.textures[textureIdentifier] === undefined) {
+ this.textures[textureIdentifier] = x3dom.Utils.createTexture2D(
+ gl, doc, url, bgnd, crossOrigin, scale, genMipMaps);
+ }
+
+ return this.textures[textureIdentifier];
+};
+
+/**
+ * Returns a Texture 2D
+ */
+x3dom.Cache.prototype.getTexture2DByDEF = function (gl, nameSpace, def) {
+ var textureIdentifier = nameSpace.name + "_" + def;
+
+ if (this.textures[textureIdentifier] === undefined) {
+ this.textures[textureIdentifier] = gl.createTexture();
+ }
+
+ return this.textures[textureIdentifier];
+};
+
+/**
+ * Returns a Cube Texture
+ */
+x3dom.Cache.prototype.getTextureCube = function (gl, doc, url, bgnd, crossOrigin, scale, genMipMaps) {
+ var textureIdentifier = "";
+
+ for (var i = 0; i < url.length; ++i) {
+ textureIdentifier += url[i] + "|";
+ }
+
+ if (this.textures[textureIdentifier] === undefined) {
+ this.textures[textureIdentifier] = x3dom.Utils.createTextureCube(
+ gl, doc, url, bgnd, crossOrigin, scale, genMipMaps);
+ }
+
+ return this.textures[textureIdentifier];
+};
+
+/**
+ * Returns one of the default shader programs
+ */
+x3dom.Cache.prototype.getShader = function (gl, shaderIdentifier) {
+ var program = null;
+
+ //Check if shader is in cache
+ if (this.shaders[shaderIdentifier] === undefined) {
+ //Choose shader based on identifier
+ switch (shaderIdentifier) {
+ case x3dom.shader.PICKING:
+ program = new x3dom.shader.PickingShader(gl);
+ break;
+ case x3dom.shader.PICKING_24:
+ program = new x3dom.shader.Picking24Shader(gl);
+ break;
+ case x3dom.shader.PICKING_ID:
+ program = new x3dom.shader.PickingIdShader(gl);
+ break;
+ case x3dom.shader.PICKING_COLOR:
+ program = new x3dom.shader.PickingColorShader(gl);
+ break;
+ case x3dom.shader.PICKING_TEXCOORD:
+ program = new x3dom.shader.PickingTexcoordShader(gl);
+ break;
+ case x3dom.shader.FRONTGROUND_TEXTURE:
+ program = new x3dom.shader.FrontgroundTextureShader(gl);
+ break;
+ case x3dom.shader.BACKGROUND_TEXTURE:
+ program = new x3dom.shader.BackgroundTextureShader(gl);
+ break;
+ case x3dom.shader.BACKGROUND_SKYTEXTURE:
+ program = new x3dom.shader.BackgroundSkyTextureShader(gl);
+ break;
+ case x3dom.shader.BACKGROUND_CUBETEXTURE:
+ program = new x3dom.shader.BackgroundCubeTextureShader(gl);
+ break;
+ case x3dom.shader.SHADOW:
+ program = new x3dom.shader.ShadowShader(gl);
+ break;
+ case x3dom.shader.BLUR:
+ program = new x3dom.shader.BlurShader(gl);
+ break;
+ case x3dom.shader.DEPTH:
+ //program = new x3dom.shader.DepthShader(gl);
+ break;
+ case x3dom.shader.NORMAL:
+ program = new x3dom.shader.NormalShader(gl);
+ break;
+ case x3dom.shader.TEXTURE_REFINEMENT:
+ program = new x3dom.shader.TextureRefinementShader(gl);
+ break;
+ default:
+ break;
+ }
+
+ if (program)
+ this.shaders[shaderIdentifier] = x3dom.Utils.wrapProgram(gl, program, shaderIdentifier);
+ else
+ x3dom.debug.logError("Couldn't create shader: " + shaderIdentifier);
+ }
+
+ return this.shaders[shaderIdentifier];
+};
+
+/**
+ * Returns a dynamic generated shader program by viewarea and shape
+ */
+x3dom.Cache.prototype.getDynamicShader = function (gl, viewarea, shape) {
+ //Generate Properties
+ var properties = x3dom.Utils.generateProperties(viewarea, shape);
+
+ var shaderID = properties.id;
+
+ if (this.shaders[shaderID] === undefined) {
+ var program;
+ if (properties.CSHADER != -1) {
+ program = new x3dom.shader.ComposedShader(gl, shape);
+ } else {
+ program = (x3dom.caps.MOBILE && !properties.CSSHADER) ?
+ new x3dom.shader.DynamicMobileShader(gl, properties) :
+ new x3dom.shader.DynamicShader(gl, properties);
+ }
+ this.shaders[shaderID] = x3dom.Utils.wrapProgram(gl, program, shaderID);
+ }
+
+ return this.shaders[shaderID];
+};
+
+/**
+ * Returns a dynamic generated shader program by properties
+ */
+x3dom.Cache.prototype.getShaderByProperties = function (gl, shape, properties, pickMode) {
+
+ //Get shaderID
+ var shaderID = properties.id;
+
+ if(pickMode != undefined || pickMode != null) {
+ shaderID += pickMode;
+ }
+
+ if (this.shaders[shaderID] === undefined)
+ {
+ var program;
+ if (properties.CSHADER != -1) {
+ program = new x3dom.shader.ComposedShader(gl, shape);
+ } else if(pickMode != undefined || pickMode != null) {
+ program = new x3dom.shader.DynamicShaderPicking(gl, properties, pickMode);
+ } else {
+ program = (x3dom.caps.MOBILE && !properties.CSSHADER) ? new x3dom.shader.DynamicMobileShader(gl, properties) :
+ new x3dom.shader.DynamicShader(gl, properties);
+ }
+ this.shaders[shaderID] = x3dom.Utils.wrapProgram(gl, program, shaderID);
+ }
+
+ return this.shaders[shaderID];
+};
+
+/**
+ * Returns the dynamically created shadow rendering shader
+ */
+x3dom.Cache.prototype.getShadowRenderingShader = function (gl, shadowedLights) {
+ var ID = "shadow";
+ for (var i = 0; i < shadowedLights.length; i++) {
+ if (x3dom.isa(shadowedLights[i], x3dom.nodeTypes.SpotLight))
+ ID += "S";
+ else if (x3dom.isa(shadowedLights[i], x3dom.nodeTypes.PointLight))
+ ID += "P";
+ else
+ ID += "D";
+ }
+
+ if (this.shaders[ID] === undefined) {
+ var program = new x3dom.shader.ShadowRenderingShader(gl, shadowedLights);
+ this.shaders[ID] = x3dom.Utils.wrapProgram(gl, program, ID);
+ }
+ return this.shaders[ID];
+};
+
+/**
+ * Release texture and shader resources
+ */
+x3dom.Cache.prototype.Release = function (gl) {
+ for (var texture in this.textures) {
+ gl.deleteTexture(this.textures[texture]);
+ }
+ this.textures = [];
+
+ for (var shaderId in this.shaders) {
+ var shader = this.shaders[shaderId];
+ var glShaders = gl.getAttachedShaders(shader.program);
+ for (var i=0; i<glShaders.length; ++i) {
+ gl.detachShader(shader.program, glShaders[i]);
+ gl.deleteShader(glShaders[i]);
+ }
+ gl.deleteProgram(shader.program)
+ }
+ this.shaders = [];
+};
+
+/*
+ * X3DOM JavaScript Library
+ * http://www.x3dom.org
+ *
+ * (C)2009 Fraunhofer IGD, Darmstadt, Germany
+ * Dual licensed under the MIT and GPL
+ *
+ * Based on code originally provided by
+ * Philip Taylor: http://philip.html5.org
+ */
+
+
+function startDashVideo(recurl, texturediv) {
+ var vars = function () {
+ var vars = {};
+ var parts = window.location.href.replace(/[?&]+([^=&]+)=([^&]*)/gi, function (m, key, value) {
+ vars[key] = value;
+ });
+ return vars;
+ },
+ url = recurl,
+ video,
+ context,
+ player;
+
+ if (vars && vars.hasOwnProperty("url")) {
+ url = vars.url;
+ }
+
+ video = document.querySelector(texturediv);
+ context = new Dash.di.DashContext();
+ player = new MediaPlayer(context);
+
+ player.startup();
+
+ player.attachView(video);
+ player.setAutoPlay(false);
+
+ player.attachSource(url);
+}
+
+
+/**
+ * Texture
+ */
+x3dom.Texture = function (gl, doc, cache, node) {
+ this.gl = gl;
+ this.doc = doc;
+ this.cache = cache;
+ this.node = node;
+
+ this.samplerName = "diffuseMap";
+ this.type = gl.TEXTURE_2D;
+ this.format = gl.RGBA;
+ this.magFilter = gl.LINEAR;
+ this.minFilter = gl.LINEAR;
+ this.wrapS = gl.REPEAT;
+ this.wrapT = gl.REPEAT;
+ this.genMipMaps = false;
+ this.texture = null;
+ this.ready = false;
+
+ this.dashtexture = false;
+
+ var tex = this.node;
+ var suffix = "mpd";
+
+ this.node._x3domTexture = this;
+
+ if (x3dom.isa(tex, x3dom.nodeTypes.MovieTexture)) {
+ // for dash we are lazy and check only the first url
+ if (tex._vf.url[0].indexOf(suffix, tex._vf.url[0].length - suffix.length) !== -1) {
+ this.dashtexture = true;
+ // we need to initially place the script for the dash player once in the document,
+ // but insert this additional script only, if really needed and Dash is requested!
+ var js = document.getElementById("AdditionalDashVideoScript");
+ if (!js) {
+ js = document.createElement("script");
+ js.setAttribute("type", "text/javascript");
+ js.setAttribute("src", x3dom.Texture.dashVideoScriptFile);
+ js.setAttribute("id", "AdditionalDashVideoScript");
+ js.onload = function() {
+ var texObj;
+ while ( (texObj = x3dom.Texture.loadDashVideos.pop()) ) {
+ x3dom.Texture.textNum++;
+ texObj.update();
+ }
+ js.ready = true;
+ };
+ document.getElementsByTagName('head')[0].appendChild(js);
+ }
+ if (js.ready === true) {
+ // count dash players and add this number to the class name for future reference
+ // (append in id too, for play, pause etc)
+ x3dom.Texture.textNum++;
+ // update can be directly called as script is already loaded
+ this.update();
+ }
+ else {
+ // push to stack and process later when script has loaded
+ x3dom.Texture.loadDashVideos.push(this);
+ }
+ }
+ }
+
+ if (!this.dashtexture) {
+ this.update();
+ }
+};
+
+x3dom.Texture.dashVideoScriptFile = "dash.all.js";
+x3dom.Texture.loadDashVideos = [];
+x3dom.Texture.textNum = 0;
+x3dom.Texture.clampFontSize = false;
+
+
+x3dom.Texture.prototype.update = function()
+{
+ if ( x3dom.isa(this.node, x3dom.nodeTypes.Text) )
+ {
+ this.updateText();
+ }
+ else
+ {
+ this.updateTexture();
+ }
+};
+
+x3dom.Texture.prototype.setPixel = function(x, y, pixel, update)
+{
+ var gl = this.gl;
+
+ var pixels = new Uint8Array(pixel);
+
+ gl.bindTexture(this.type, this.texture);
+
+ gl.pixelStorei(gl.UNPACK_ALIGNMENT, 1);
+
+ gl.texSubImage2D(this.type, 0, x, y, 1, 1, this.format, gl.UNSIGNED_BYTE, pixels);
+
+ gl.bindTexture(this.type, null);
+
+ if(update) {
+ this.doc.needRender = true;
+ }
+};
+
+x3dom.Texture.prototype.updateTexture = function()
+{
+ var gl = this.gl;
+ var doc = this.doc;
+ var tex = this.node;
+
+ //Set sampler
+ this.samplerName = tex._type;
+
+ //Set texture type
+ if ( x3dom.isa(tex, x3dom.nodeTypes.X3DEnvironmentTextureNode) ) {
+ this.type = gl.TEXTURE_CUBE_MAP;
+ } else {
+ this.type = gl.TEXTURE_2D;
+ }
+
+ //Set texture format
+ if (x3dom.isa(tex, x3dom.nodeTypes.PixelTexture)) {
+ switch (tex._vf.image.comp)
+ {
+ case 1: this.format = gl.LUMINANCE; break;
+ case 2: this.format = gl.LUMINANCE_ALPHA; break;
+ case 3: this.format = gl.RGB; break;
+ case 4: this.format = gl.RGBA; break;
+ }
+ } else {
+ this.format = gl.RGBA;
+ }
+
+ //Set texture min, mag, wrapS and wrapT
+ if (tex._cf.textureProperties.node !== null) {
+ var texProp = tex._cf.textureProperties.node;
+
+ this.wrapS = x3dom.Utils.boundaryModesDic(gl, texProp._vf.boundaryModeS);
+ this.wrapT = x3dom.Utils.boundaryModesDic(gl, texProp._vf.boundaryModeT);
+
+ this.minFilter = x3dom.Utils.minFilterDic(gl, texProp._vf.minificationFilter);
+ this.magFilter = x3dom.Utils.magFilterDic(gl, texProp._vf.magnificationFilter);
+
+ if (texProp._vf.generateMipMaps === true) {
+ this.genMipMaps = true;
+
+ if (this.minFilter == gl.NEAREST) {
+ this.minFilter = gl.NEAREST_MIPMAP_NEAREST;
+ } else if (this.minFilter == gl.LINEAR) {
+ this.minFilter = gl.LINEAR_MIPMAP_LINEAR;
+ }
+
+ if (this.texture && (this.texture.ready || this.texture.textureCubeReady)) {
+ gl.bindTexture(this.type, this.texture);
+ gl.generateMipmap(this.type);
+ gl.bindTexture(this.type, null);
+ }
+ } else {
+ this.genMipMaps = false;
+
+ if ( (this.minFilter == gl.LINEAR_MIPMAP_LINEAR) ||
+ (this.minFilter == gl.LINEAR_MIPMAP_NEAREST) ) {
+ this.minFilter = gl.LINEAR;
+ } else if ( (this.minFilter == gl.NEAREST_MIPMAP_LINEAR) ||
+ (this.minFilter == gl.NEAREST_MIPMAP_NEAREST) ) {
+ this.minFilter = gl.NEAREST;
+ }
+ }
+ } else {
+ if (tex._vf.repeatS == false) {
+ this.wrapS = gl.CLAMP_TO_EDGE;
+ }
+ else
+ {
+ this.wrapS = gl.REPEAT;
+ }
+ if (tex._vf.repeatT == false) {
+ this.wrapT = gl.CLAMP_TO_EDGE;
+ }
+ else
+ {
+ this.wrapT = gl.REPEAT;
+ }
+
+ if (this.samplerName == "displacementMap" ||
+ this.samplerName == "multiDiffuseAlphaMap" ||
+ this.samplerName == "multiVisibilityMap" ||
+ this.samplerName == "multiEmissiveAmbientMap" ||
+ this.samplerName == "multiSpecularShininessMap")
+ {
+ this.wrapS = gl.CLAMP_TO_EDGE;
+ this.wrapT = gl.CLAMP_TO_EDGE;
+ this.minFilter = gl.NEAREST;
+ this.magFilter = gl.NEAREST;
+ }
+ }
+
+ //Looking for child texture
+ var childTex = (tex._video && tex._needPerFrameUpdate === true);
+
+ //Set texture
+ if (tex._isCanvas && tex._canvas)
+ {
+ if (this.texture == null) {
+ this.texture = gl.createTexture()
+ }
+ this.texture.width = tex._canvas.width;
+ this.texture.height = tex._canvas.height;
+ this.texture.ready = true;
+
+ gl.bindTexture(this.type, this.texture);
+ gl.texImage2D(this.type, 0, this.format, this.format, gl.UNSIGNED_BYTE, tex._canvas);
+ if (this.genMipMaps) {
+ gl.generateMipmap(this.type);
+ }
+ gl.bindTexture(this.type, null);
+ }
+ else if (x3dom.isa(tex, x3dom.nodeTypes.RenderedTexture))
+ {
+ if (tex._webgl && tex._webgl.fbo) {
+ this.texture = tex._webgl.fbo.tex;
+ }
+ else {
+ this.texture = null;
+ x3dom.debug.logError("Try updating RenderedTexture without FBO initialized!");
+ }
+ if (this.texture) {
+ this.texture.ready = true;
+ }
+ }
+ else if (x3dom.isa(tex, x3dom.nodeTypes.PixelTexture))
+ {
+ if (this.texture == null) {
+ if (this.node._DEF) {
+ this.texture = this.cache.getTexture2DByDEF(gl, this.node._nameSpace, this.node._DEF);
+ } else {
+ this.texture = gl.createTexture();
+ }
+ }
+ this.texture.width = tex._vf.image.width;
+ this.texture.height = tex._vf.image.height;
+ this.texture.ready = true;
+
+ var pixelArr = tex._vf.image.array;//.toGL();
+ var pixelArrfont_size = tex._vf.image.width * tex._vf.image.height * tex._vf.image.comp;
+
+ if (pixelArr.length < pixelArrfont_size)
+ {
+ pixelArr = tex._vf.image.toGL();
+
+ while (pixelArr.length < pixelArrfont_size) {
+ pixelArr.push(0);
+ }
+ }
+
+ var pixels = new Uint8Array(pixelArr);
+
+ gl.bindTexture(this.type, this.texture);
+ gl.pixelStorei(gl.UNPACK_ALIGNMENT, 1);
+ gl.texImage2D(this.type, 0, this.format,
+ tex._vf.image.width, tex._vf.image.height, 0,
+ this.format, gl.UNSIGNED_BYTE, pixels);
+ if (this.genMipMaps) {
+ gl.generateMipmap(this.type);
+ }
+ gl.bindTexture(this.type, null);
+ }
+ else if (x3dom.isa(tex, x3dom.nodeTypes.MovieTexture) || childTex)
+ {
+ var that = this;
+ var p = document.getElementsByTagName('body')[0];
+
+ if (this.texture == null) {
+ this.texture = gl.createTexture();
+ }
+
+ if (this.dashtexture) {
+ var element_vid = document.createElement('div');
+ element_vid.setAttribute('class', 'dash-video-player' + x3dom.Texture.textNum);
+ tex._video = document.createElement('video');
+ tex._video.setAttribute('preload', 'auto');
+ tex._video.setAttribute('muted', 'muted');
+
+ var scriptToRun = document.createElement('script');
+ scriptToRun.setAttribute('type', 'text/javascript');
+ scriptToRun.innerHTML = 'startDashVideo("' + tex._vf.url[0] +
+ '",".dash-video-player' + x3dom.Texture.textNum + ' video")';
+ element_vid.appendChild(scriptToRun);
+ element_vid.appendChild(tex._video);
+ p.appendChild(element_vid);
+ tex._video.style.visibility = "hidden";
+ tex._video.style.display = "none";
+ }
+ else {
+ if (!childTex) {
+ tex._video = document.createElement('video');
+ tex._video.setAttribute('preload', 'auto');
+ tex._video.setAttribute('muted', 'muted');
+ p.appendChild(tex._video);
+ tex._video.style.visibility = "hidden";
+ tex._video.style.display = "none";
+ }
+ for (var i = 0; i < tex._vf.url.length; i++) {
+ var videoUrl = tex._nameSpace.getURL(tex._vf.url[i]);
+ x3dom.debug.logInfo('Adding video file: ' + videoUrl);
+ var src = document.createElement('source');
+ src.setAttribute('src', videoUrl);
+ tex._video.appendChild(src);
+ }
+ }
+
+ var updateMovie = function()
+ {
+ gl.bindTexture(that.type, that.texture);
+ gl.texImage2D(that.type, 0, that.format, that.format, gl.UNSIGNED_BYTE, tex._video);
+ if (that.genMipMaps) {
+ gl.generateMipmap(that.type);
+ }
+ gl.bindTexture(that.type, null);
+ that.texture.ready = true;
+ doc.needRender = true;
+ };
+
+ var startVideo = function()
+ {
+ tex._video.play();
+ tex._intervalID = setInterval(updateMovie, 16);
+ };
+
+ var videoDone = function()
+ {
+ clearInterval(tex._intervalID);
+ if (tex._vf.loop === true)
+ {
+ tex._video.play();
+ tex._intervalID = setInterval(updateMovie, 16);
+ }
+ };
+
+ // Start listening for the canplaythrough event, so we do not
+ // start playing the video until we can do so without stuttering
+ tex._video.addEventListener("canplaythrough", startVideo, true);
+
+ // Start listening for the ended event, so we can stop the
+ // texture update when the video is finished playing
+ tex._video.addEventListener("ended", videoDone, true);
+ }
+ else if (x3dom.isa(tex, x3dom.nodeTypes.X3DEnvironmentTextureNode))
+ {
+ this.texture = this.cache.getTextureCube(gl, doc, tex.getTexUrl(), false,
+ tex._vf.crossOrigin, tex._vf.scale, this.genMipMaps);
+ }
+ else
+ {
+ this.texture = this.cache.getTexture2D(gl, doc, tex._nameSpace.getURL(tex._vf.url[0]),
+ false, tex._vf.crossOrigin, tex._vf.scale, this.genMipMaps);
+ }
+};
+
+x3dom.Texture.prototype.updateText = function()
+{
+ var gl = this.gl;
+
+ this.wrapS = gl.CLAMP_TO_EDGE;
+ this.wrapT = gl.CLAMP_TO_EDGE;
+
+ var fontStyleNode = this.node._cf.fontStyle.node;
+
+ var font_family = 'serif';
+ var font_style = 'normal';
+ var font_justify = 'left';
+ var font_size = 1.0;
+ var font_spacing = 1.0;
+ var font_horizontal = true;
+ var font_language = "";
+
+ if ( fontStyleNode !== null )
+ {
+ var fonts = fontStyleNode._vf.family.toString();
+
+ // clean attribute values and split in array
+ fonts = fonts.trim().replace(/\'/g,'').replace(/\,/, ' ');
+ fonts = fonts.split(" ");
+
+ font_family = Array.map(fonts, function(s) {
+ if (s == 'SANS') { return 'sans-serif'; }
+ else if (s == 'SERIF') { return 'serif'; }
+ else if (s == 'TYPEWRITER') { return 'monospace'; }
+ else { return ''+s+''; } //'Verdana'
+ }).join(",");
+
+ font_style = fontStyleNode._vf.style.toString().replace(/\'/g,'');
+ switch (font_style.toUpperCase()) {
+ case 'PLAIN': font_style = 'normal'; break;
+ case 'BOLD': font_style = 'bold'; break;
+ case 'ITALIC': font_style = 'italic'; break;
+ case 'BOLDITALIC': font_style = 'italic bold'; break;
+ default: font_style = 'normal';
+ }
+
+ var leftToRight = fontStyleNode._vf.leftToRight ? 'ltr' : 'rtl';
+ var topToBottom = fontStyleNode._vf.topToBottom;
+
+ // TODO: make it possible to use multiple values
+ font_justify = fontStyleNode._vf.justify[0].toString().replace(/\'/g,'');
+
+ switch (font_justify.toUpperCase()) {
+ case 'BEGIN': font_justify = 'left'; break;
+ case 'END': font_justify = 'right'; break;
+ case 'FIRST': font_justify = 'left'; break; // not clear what to do with this one
+ case 'MIDDLE': font_justify = 'center'; break;
+ default: font_justify = 'left'; break;
+ }
+
+ font_size = fontStyleNode._vf.size;
+ font_spacing = fontStyleNode._vf.spacing;
+ font_horizontal = fontStyleNode._vf.horizontal;
+ font_language = fontStyleNode._vf.language;
+
+ if (font_size < 0.1) font_size = 0.1;
+ if(x3dom.Texture.clampFontSize && font_size > 2.3)
+ {
+ font_size = 2.3;
+ }
+ }
+
+ var textX, textY;
+ var paragraph = this.node._vf.string;
+ var text_canvas = document.createElement('canvas');
+ text_canvas.dir = leftToRight;
+ var textHeight = font_size * 42; // pixel size relative to local coordinate system
+ var textAlignment = font_justify;
+
+ // needed to make webfonts work
+ document.body.appendChild(text_canvas);
+
+ var text_ctx = text_canvas.getContext('2d');
+
+ // calculate font font_size in px
+ text_ctx.font = font_style + " " + textHeight + "px " + font_family;
+
+ var maxWidth = text_ctx.measureText(paragraph[0]).width;
+ var i;
+
+ // calculate maxWidth
+ for(i = 1; i < paragraph.length; i++) {
+ if(text_ctx.measureText(paragraph[i]).width > maxWidth)
+ maxWidth = text_ctx.measureText(paragraph[i]).width;
+ }
+ var canvas_scale = 1.1; //needed for some fonts that are higher than the textHeight
+ text_canvas.width = maxWidth * canvas_scale;
+ text_canvas.height = textHeight * paragraph.length * canvas_scale;
+
+ switch(textAlignment) {
+ case "left": textX = 0; break;
+ case "center": textX = text_canvas.width/2; break;
+ case "right": textX = text_canvas.width; break;
+ }
+
+ var txtW = text_canvas.width;
+ var txtH = text_canvas.height;
+
+ text_ctx.fillStyle = 'rgba(0,0,0,0)';
+ text_ctx.fillRect(0, 0, text_ctx.canvas.width, text_ctx.canvas.height);
+
+ // write white text with black border
+ text_ctx.fillStyle = 'white';
+ text_ctx.lineWidth = 2.5;
+ text_ctx.strokeStyle = 'grey';
+ text_ctx.textBaseline = 'top';
+
+ text_ctx.font = font_style + " " + textHeight + "px " + font_family;
+ text_ctx.textAlign = textAlignment;
+
+ // create the multiline text
+ for(i = 0; i < paragraph.length; i++) {
+ textY = i*textHeight;
+ text_ctx.fillText(paragraph[i], textX, textY);
+ }
+
+ if( this.texture === null )
+ {
+ this.texture = gl.createTexture();
+ }
+
+ gl.bindTexture(this.type, this.texture);
+ gl.texImage2D(this.type, 0, this.format, this.format, gl.UNSIGNED_BYTE, text_canvas);
+ gl.bindTexture(this.type, null);
+
+ //remove canvas after Texture creation
+ document.body.removeChild(text_canvas);
+
+ var w = txtW / 100.0;
+ var h = txtH / 100.0;
+
+ this.node._mesh._positions[0] = [-w,-h+.4,0, w,-h+.4,0, w,h+.4,0, -w,h+.4,0];
+
+ this.node.invalidateVolume();
+ Array.forEach(this.node._parentNodes, function (node) {
+ node.setAllDirty();
+ });
+};
+
+/*
+ * X3DOM JavaScript Library
+ * http://www.x3dom.org
+ *
+ * (C)2009 Fraunhofer IGD, Darmstadt, Germany
+ * Dual licensed under the MIT and GPL
+ *
+ * Based on code originally provided by
+ * Philip Taylor: http://philip.html5.org
+ */
+
+
+// ### X3DDocument ###
+x3dom.X3DDocument = function(canvas, ctx, settings) {
+ this.canvas = canvas; // The <canvas> elem
+ this.ctx = ctx; // WebGL context object, AKA gl
+ this.properties = settings; // showStat, showLog, etc.
+ this.needRender = true; // Trigger redraw if true
+ this._x3dElem = null; // Backref to <X3D> root element (set on parsing)
+ this._scene = null; // Scene root element
+ this._viewarea = null; // Viewport, handles rendering and interaction
+ this.downloadCount = 0; // Counter for objects to be loaded
+
+ // bag for pro-active (or multi-core-like) elements
+ this._nodeBag = {
+ timer: [], // TimeSensor (tick)
+ lights: [], // Light
+ clipPlanes: [], // ClipPlane
+ followers: [], // X3DFollowerNode
+ trans: [], // X3DTransformNode (for listening to CSS changes)
+ renderTextures: [], // RenderedTexture
+ viewarea: [], // Viewport (for updating camera navigation)
+ affectedPointingSensors: [] // all X3DPointingDeviceSensor currently activated (i.e., used for interaction),
+ // this list is maintained for efficient update / deactivation
+ };
+
+ this.onload = function () {};
+ this.onerror = function () {};
+};
+
+x3dom.X3DDocument.prototype.load = function (uri, sceneElemPos) {
+ // Load uri. Get sceneDoc, list of sub-URIs.
+ // For each URI, get docs[uri] = whatever, extend list of sub-URIs.
+
+ var uri_docs = {};
+ var queued_uris = [uri];
+ var doc = this;
+
+ function next_step() {
+ // TODO: detect circular inclusions
+ // TODO: download in parallel where possible
+
+ if (queued_uris.length === 0) {
+ // All done
+ doc._setup(uri_docs[uri], uri_docs, sceneElemPos);
+ doc.onload();
+ return;
+ }
+ var next_uri = queued_uris.shift();
+
+ if ( x3dom.isX3DElement(next_uri) &&
+ (next_uri.localName.toLowerCase() === 'x3d' || next_uri.localName.toLowerCase() === 'websg') )
+ {
+ // Special case, when passed an X3D node instead of a URI string
+ uri_docs[next_uri] = next_uri;
+ doc._x3dElem = next_uri;
+ next_step();
+ }
+ }
+
+ next_step();
+};
+
+x3dom.findScene = function(x3dElem) {
+ var sceneElems = [];
+
+ for (var i=0; i<x3dElem.childNodes.length; i++) {
+ var sceneElem = x3dElem.childNodes[i];
+
+ if (sceneElem && sceneElem.localName && sceneElem.localName.toLowerCase() === "scene") {
+ sceneElems.push(sceneElem);
+ }
+ }
+
+ if (sceneElems.length > 1) {
+ x3dom.debug.logError("X3D element has more than one Scene child (has " +
+ x3dElem.childNodes.length + ").");
+ }
+ else {
+ return sceneElems[0];
+ }
+ return null;
+};
+
+
+x3dom.X3DDocument.prototype._setup = function (sceneDoc, uriDocs, sceneElemPos) {
+ var doc = this;
+
+ function cleanNodeBag(bag, node) {
+ for (var i=0, n=bag.length; i<n; i++) {
+ if (bag[i] === node) {
+ bag.splice(i, 1);
+ break;
+ }
+ }
+ }
+
+ function removeX3DOMBackendGraph(domNode) {
+ var children = domNode.childNodes;
+
+ for (var i=0, n=children.length; i<n; i++) {
+ removeX3DOMBackendGraph(children[i]);
+ }
+
+ if (domNode._x3domNode) {
+ var node = domNode._x3domNode;
+ var nameSpace = node._nameSpace;
+
+ if (x3dom.isa(node, x3dom.nodeTypes.X3DShapeNode)) {
+ if (node._cleanupGLObjects) {
+ node._cleanupGLObjects(true);
+ // TODO: more cleanups, e.g. texture/shader cache?
+ }
+ if (x3dom.nodeTypes.Shape.idMap.nodeID[node._objectID]) {
+ delete x3dom.nodeTypes.Shape.idMap.nodeID[node._objectID];
+ }
+ }
+ else if (x3dom.isa(node, x3dom.nodeTypes.TimeSensor)) {
+ cleanNodeBag(doc._nodeBag.timer, node);
+ }
+ else if (x3dom.isa(node, x3dom.nodeTypes.X3DLightNode)) {
+ cleanNodeBag(doc._nodeBag.lights, node);
+ }
+ else if (x3dom.isa(node, x3dom.nodeTypes.X3DFollowerNode)) {
+ cleanNodeBag(doc._nodeBag.followers, node);
+ }
+ else if (x3dom.isa(node, x3dom.nodeTypes.X3DTransformNode)) {
+ cleanNodeBag(doc._nodeBag.trans, node);
+ }
+ else if (x3dom.isa(node, x3dom.nodeTypes.RenderedTexture)) {
+ cleanNodeBag(doc._nodeBag.renderTextures, node);
+ if (node._cleanupGLObjects) {
+ node._cleanupGLObjects();
+ }
+ }
+ else if (x3dom.isa(node, x3dom.nodeTypes.X3DPointingDeviceSensorNode)) {
+ cleanNodeBag(doc._nodeBag.affectedPointingSensors, node);
+ }
+ else if (x3dom.isa(node, x3dom.nodeTypes.Texture)) {
+ node.shutdown(); // general texture might have video
+ }
+ else if (x3dom.isa(node, x3dom.nodeTypes.AudioClip)) {
+ node.shutdown();
+ }
+ else if (x3dom.isa(node, x3dom.nodeTypes.X3DBindableNode)) {
+ var stack = node._stack;
+ if (stack) {
+ node.bind(false);
+ cleanNodeBag(stack._bindBag, node);
+ }
+ // Background may have geometry
+ if (node._cleanupGLObjects) {
+ node._cleanupGLObjects();
+ }
+ }
+ else if (x3dom.isa(node, x3dom.nodeTypes.Scene)) {
+ if (node._webgl) {
+ node._webgl = null;
+ // TODO; explicitly delete all gl objects
+ }
+ }
+
+ //do not remove node from namespace if it was only "USE"d
+ if (nameSpace && ! domNode.getAttribute('use'))
+ {
+ nameSpace.removeNode(node._DEF);
+ }
+ node._xmlNode = null;
+
+ delete domNode._x3domNode;
+ }
+ }
+
+ // Test capturing DOM mutation events on the X3D subscene
+ var domEventListener = {
+ onAttrModified: function(e) {
+ if ('_x3domNode' in e.target) {
+ var attrToString = {
+ 1: "MODIFICATION",
+ 2: "ADDITION",
+ 3: "REMOVAL"
+ };
+ //x3dom.debug.logInfo("MUTATION: " + e.attrName + ", " + e.type + ", attrChange=" + attrToString[e.attrChange]);
+ e.target._x3domNode.updateField(e.attrName, e.newValue);
+ doc.needRender = true;
+ }
+ },
+
+ onNodeRemoved: function(e) {
+ var domNode = e.target;
+ if (!domNode)
+ return;
+
+ if ('_x3domNode' in domNode.parentNode && '_x3domNode' in domNode) {
+ var parent = domNode.parentNode._x3domNode;
+ var child = domNode._x3domNode;
+
+ if (parent && child) {
+ parent.removeChild(child);
+ parent.nodeChanged();
+
+ removeX3DOMBackendGraph(domNode);
+
+ if (doc._viewarea && doc._viewarea._scene) {
+ doc._viewarea._scene.nodeChanged();
+ doc._viewarea._scene.updateVolume();
+ doc.needRender = true;
+ }
+ }
+ }
+ else if (domNode.localName && domNode.localName.toUpperCase() == "ROUTE" && domNode._nodeNameSpace) {
+ var fromNode = domNode._nodeNameSpace.defMap[domNode.getAttribute('fromNode')];
+ var toNode = domNode._nodeNameSpace.defMap[domNode.getAttribute('toNode')];
+
+ if (fromNode && toNode) {
+ fromNode.removeRoute(domNode.getAttribute('fromField'), toNode, domNode.getAttribute('toField'));
+ }
+ }
+ else if (domNode.localName && domNode.localName.toUpperCase() == "X3D") {
+ var runtime = domNode.runtime;
+
+ if (runtime && runtime.canvas && runtime.canvas.doc && runtime.canvas.doc._scene) {
+ var sceneNode = runtime.canvas.doc._scene._xmlNode;
+
+ removeX3DOMBackendGraph(sceneNode);
+
+ // also clear corresponding X3DCanvas element
+ for (var i=0; i<x3dom.canvases.length; i++) {
+ if (x3dom.canvases[i] === runtime.canvas) {
+ x3dom.canvases[i].doc.shutdown(x3dom.canvases[i].gl);
+ x3dom.canvases.splice(i, 1);
+ break;
+ }
+ }
+
+ runtime.canvas.doc._scene = null;
+ runtime.canvas.doc._viewarea = null;
+ runtime.canvas.doc = null;
+ runtime.canvas = null;
+ runtime = null;
+
+ domNode.context = null;
+ domNode.runtime = null;
+ }
+ }
+ },
+
+ onNodeInserted: function(e) {
+ var child = e.target;
+ var parentNode = child.parentNode;
+
+ // only act on x3dom nodes, ignore regular HTML
+ if ('_x3domNode' in parentNode) {
+ if (parentNode.tagName && parentNode.tagName.toLowerCase() == 'inline' ||
+ parentNode.tagName.toLowerCase() == 'multipart') {
+ // do nothing
+ }
+ else {
+ var parent = parentNode._x3domNode;
+
+ if (parent && parent._nameSpace && (child instanceof Element)) {
+
+ if (x3dom.caps.DOMNodeInsertedEvent_perSubtree)
+ {
+ removeX3DOMBackendGraph(child); // not really necessary...
+ }
+
+ var newNode = parent._nameSpace.setupTree(child);
+
+ parent.addChild(newNode, child.getAttribute("containerField"));
+ parent.nodeChanged();
+
+ var grandParentNode = parentNode.parentNode;
+ if (grandParentNode && grandParentNode._x3domNode)
+ grandParentNode._x3domNode.nodeChanged();
+
+ if (doc._viewarea && doc._viewarea._scene) {
+ doc._viewarea._scene.nodeChanged();
+ doc._viewarea._scene.updateVolume();
+ doc.needRender = true;
+ }
+ }
+ else {
+ x3dom.debug.logWarning("No _nameSpace in onNodeInserted");
+ }
+ }
+ }
+ }
+ };
+
+ //sceneDoc.addEventListener('DOMCharacterDataModified', domEventListener.onAttrModified, true);
+ sceneDoc.addEventListener('DOMNodeRemoved', domEventListener.onNodeRemoved, true);
+ sceneDoc.addEventListener('DOMNodeInserted', domEventListener.onNodeInserted, true);
+ if ( (x3dom.userAgentFeature.supportsDOMAttrModified === true ) ) {
+ sceneDoc.addEventListener('DOMAttrModified', domEventListener.onAttrModified, true);
+ }
+
+ // sceneDoc is the X3D element here...
+ var sceneElem = x3dom.findScene(sceneDoc);
+
+ // create and add BindableBag that holds all bindable stacks
+ this._bindableBag = new x3dom.BindableBag(this);
+
+ // create and add the NodeNameSpace
+ var nameSpace = new x3dom.NodeNameSpace("scene", doc);
+
+ var scene = nameSpace.setupTree(sceneElem);
+
+ // link scene
+ this._scene = scene;
+ this._bindableBag.setRefNode(scene);
+
+ // create view
+ this._viewarea = new x3dom.Viewarea (this, scene);
+
+ this._viewarea._width = this.canvas.width;
+ this._viewarea._height = this.canvas.height;
+};
+
+x3dom.X3DDocument.prototype.advanceTime = function (t) {
+ var i = 0;
+
+ if (this._nodeBag.timer.length) {
+ for (i=0; i < this._nodeBag.timer.length; i++)
+ { this.needRender |= this._nodeBag.timer[i].tick(t); }
+ }
+ if (this._nodeBag.followers.length) {
+ for (i=0; i < this._nodeBag.followers.length; i++)
+ { this.needRender |= this._nodeBag.followers[i].tick(t); }
+ }
+ // just a temporary tricker solution to update the CSS transforms
+ if (this._nodeBag.trans.length) {
+ for (i=0; i < this._nodeBag.trans.length; i++)
+ { this.needRender |= this._nodeBag.trans[i].tick(t); }
+ }
+ if (this._nodeBag.viewarea.length) {
+ for (i=0; i < this._nodeBag.viewarea.length; i++)
+ { this.needRender |= this._nodeBag.viewarea[i].tick(t); }
+ }
+};
+
+x3dom.X3DDocument.prototype.render = function (ctx) {
+ if (!ctx || !this._viewarea) {
+ return;
+ }
+
+ ctx.renderScene(this._viewarea);
+};
+
+x3dom.X3DDocument.prototype.onPick = function (ctx, x, y) {
+ if (!ctx || !this._viewarea) {
+ return;
+ }
+
+ ctx.pickValue(this._viewarea, x, y, 1);
+};
+
+x3dom.X3DDocument.prototype.onPickRect = function (ctx, x1, y1, x2, y2) {
+ if (!ctx || !this._viewarea) {
+ return [];
+ }
+
+ return ctx.pickRect(this._viewarea, x1, y1, x2, y2);
+};
+
+x3dom.X3DDocument.prototype.onMove = function (ctx, x, y, buttonState) {
+ if (!ctx || !this._viewarea) {
+ return;
+ }
+
+ if (this._viewarea._scene._vf.doPickPass)
+ ctx.pickValue(this._viewarea, x, y, buttonState);
+ this._viewarea.onMove(x, y, buttonState);
+};
+
+x3dom.X3DDocument.prototype.onMoveView = function (ctx, translation, rotation) {
+ if (!ctx || !this._viewarea) {
+ return;
+ }
+
+ this._viewarea.onMoveView(translation, rotation);
+};
+
+x3dom.X3DDocument.prototype.onDrag = function (ctx, x, y, buttonState) {
+ if (!ctx || !this._viewarea) {
+ return;
+ }
+
+ if (this._viewarea._scene._vf.doPickPass)
+ ctx.pickValue(this._viewarea, x, y, buttonState);
+ this._viewarea.onDrag(x, y, buttonState);
+};
+
+x3dom.X3DDocument.prototype.onWheel = function (ctx, x, y, originalY) {
+ if (!ctx || !this._viewarea) {
+ return;
+ }
+
+ if (this._viewarea._scene._vf.doPickPass)
+ ctx.pickValue(this._viewarea, x, originalY, 0);
+ this._viewarea.onDrag(x, y, 2);
+};
+
+x3dom.X3DDocument.prototype.onMousePress = function (ctx, x, y, buttonState) {
+ if (!ctx || !this._viewarea) {
+ return;
+ }
+
+ // update volume only on click since expensive!
+ this._viewarea._scene.updateVolume();
+
+ ctx.pickValue(this._viewarea, x, y, buttonState);
+ this._viewarea.onMousePress(x, y, buttonState);
+};
+
+x3dom.X3DDocument.prototype.onMouseRelease = function (ctx, x, y, buttonState, prevButton) {
+ if (!ctx || !this._viewarea) {
+ return;
+ }
+
+ var button = (prevButton << 8) | buttonState; // for shadowObjectIdChanged
+ ctx.pickValue(this._viewarea, x, y, button);
+ this._viewarea.onMouseRelease(x, y, buttonState, prevButton);
+};
+
+x3dom.X3DDocument.prototype.onMouseOver = function (ctx, x, y, buttonState) {
+ if (!ctx || !this._viewarea) {
+ return;
+ }
+
+ ctx.pickValue(this._viewarea, x, y, buttonState);
+ this._viewarea.onMouseOver(x, y, buttonState);
+};
+
+x3dom.X3DDocument.prototype.onMouseOut = function (ctx, x, y, buttonState) {
+ if (!ctx || !this._viewarea) {
+ return;
+ }
+
+ ctx.pickValue(this._viewarea, x, y, buttonState);
+ this._viewarea.onMouseOut(x, y, buttonState);
+};
+
+x3dom.X3DDocument.prototype.onDoubleClick = function (ctx, x, y) {
+ if (!ctx || !this._viewarea) {
+ return;
+ }
+
+ this._viewarea.onDoubleClick(x, y);
+};
+
+
+x3dom.X3DDocument.prototype.onKeyDown = function(keyCode)
+{
+ //x3dom.debug.logInfo("pressed key " + keyCode);
+ switch (keyCode) {
+ case 37: /* left */
+ this._viewarea.strafeLeft();
+ break;
+ case 38: /* up */
+ this._viewarea.moveFwd();
+ break;
+ case 39: /* right */
+ this._viewarea.strafeRight();
+ break;
+ case 40: /* down */
+ this._viewarea.moveBwd();
+ break;
+ default:
+ }
+};
+
+x3dom.X3DDocument.prototype.onKeyUp = function(keyCode)
+{
+ //x3dom.debug.logInfo("released key " + keyCode);
+ var stack = null;
+
+ switch (keyCode) {
+ case 13: /* return */
+ x3dom.toggleFullScreen();
+ break;
+ case 27: /* ESC */
+ window.history.back(); // emulate good old ESC key
+ break;
+ case 33: /* page up */
+ stack = this._scene.getViewpoint()._stack;
+
+ if (stack) {
+ stack.switchTo('next');
+ }
+ else {
+ x3dom.debug.logError ('No valid ViewBindable stack.');
+ }
+ break;
+ case 34: /* page down */
+ stack = this._scene.getViewpoint()._stack;
+
+ if (stack) {
+ stack.switchTo('prev');
+ }
+ else {
+ x3dom.debug.logError ('No valid ViewBindable stack.');
+ }
+ break;
+ case 37: /* left */
+ break;
+ case 38: /* up */
+ break;
+ case 39: /* right */
+ break;
+ case 40: /* down */
+ break;
+ default:
+ }
+};
+
+x3dom.X3DDocument.prototype.onKeyPress = function(charCode)
+{
+ //x3dom.debug.logInfo("pressed key " + charCode);
+ var nav = this._scene.getNavigationInfo();
+ var env = this._scene.getEnvironment();
+
+ switch (charCode)
+ {
+ case 32: /* space */
+ var states = this.canvas.parent.stateViewer;
+ if (states) {
+ states.display();
+ }
+ x3dom.debug.logInfo("a: show all | d: show helper buffers | s: small feature culling | t: light view | " +
+ "m: toggle render mode | c: frustum culling | p: intersect type | r: reset view | \n" +
+ "e: examine mode | f: fly mode | y: freefly mode | w: walk mode | h: helicopter mode | " +
+ "l: lookAt mode | o: lookaround | g: game mode | n: turntable | u: upright position | \n" +
+ "v: print viewpoint info | pageUp: next view | pageDown: prev. view | " +
+ "+: increase speed | -: decrease speed ");
+ break;
+ case 43: /* + (incr. speed) */
+ nav._vf.speed = 2 * nav._vf.speed;
+ x3dom.debug.logInfo("Changed navigation speed to " + nav._vf.speed);
+ break;
+ case 45: /* - (decr. speed) */
+ nav._vf.speed = 0.5 * nav._vf.speed;
+ x3dom.debug.logInfo("Changed navigation speed to " + nav._vf.speed);
+ break;
+ case 51: /* 3 (decr pg error tol) */
+ x3dom.nodeTypes.PopGeometry.ErrorToleranceFactor += 0.5;
+ x3dom.debug.logInfo("Changed POP error tolerance to " + x3dom.nodeTypes.PopGeometry.ErrorToleranceFactor);
+ break;
+ case 52: /* 4 (incr pg error tol) */
+ x3dom.nodeTypes.PopGeometry.ErrorToleranceFactor -= 0.5;
+ x3dom.debug.logInfo("Changed POP error tolerance to " + x3dom.nodeTypes.PopGeometry.ErrorToleranceFactor);
+ break;
+ case 54: /* 6 (incr height) */
+ nav._vf.typeParams[1] += 1.0;
+ nav._heliUpdated = false;
+ x3dom.debug.logInfo("Changed helicopter height to " + nav._vf.typeParams[1]);
+ break;
+ case 55: /* 7 (decr height) */
+ nav._vf.typeParams[1] -= 1.0;
+ nav._heliUpdated = false;
+ x3dom.debug.logInfo("Changed helicopter height to " + nav._vf.typeParams[1]);
+ break;
+ case 56: /* 8 (decr angle) */
+ nav._vf.typeParams[0] -= 0.02;
+ nav._heliUpdated = false;
+ x3dom.debug.logInfo("Changed helicopter angle to " + nav._vf.typeParams[0]);
+ break;
+ case 57: /* 9 (incr angle) */
+ nav._vf.typeParams[0] += 0.02;
+ nav._heliUpdated = false;
+ x3dom.debug.logInfo("Changed helicopter angle to " + nav._vf.typeParams[0]);
+ break;
+ case 97: /* a, view all */
+ this._viewarea.showAll();
+ break;
+ case 99: /* c, toggle frustum culling */
+ env._vf.frustumCulling = !env._vf.frustumCulling;
+ x3dom.debug.logInfo("Viewfrustum culling " + (env._vf.frustumCulling ? "on" : "off"));
+ break;
+ case 100: /* d, switch on/off buffer view for dbg */
+ if (this._viewarea._visDbgBuf === undefined) {
+ this._viewarea._visDbgBuf = (this._x3dElem.getAttribute("showLog") === 'true');
+ }
+ this._viewarea._visDbgBuf = !this._viewarea._visDbgBuf;
+ x3dom.debug.logContainer.style.display = (this._viewarea._visDbgBuf == true) ? "block" : "none";
+ break;
+ case 101: /* e, examine mode */
+ nav.setType("examine", this._viewarea);
+ break;
+ case 102: /* f, fly mode */
+ nav.setType("fly", this._viewarea);
+ break;
+ case 103: /* g, game mode */
+ nav.setType("game", this._viewarea);
+ break;
+ case 104: /* h, helicopter mode */
+ nav.setType("helicopter", this._viewarea);
+ break;
+ case 105: /* i, fit all */
+ this._viewarea.fit(this._scene._lastMin, this._scene._lastMax);
+ break;
+ case 108: /* l, lookAt mode */
+ nav.setType("lookat", this._viewarea);
+ break;
+ case 109: /* m, toggle "points" attribute */
+ this._viewarea._points = ++this._viewarea._points % 3;
+ break;
+ case 110: /* n, turntable */
+ nav.setType("turntable", this._viewarea);
+ break;
+ case 111: /* o, look around like in fly, but don't move */
+ nav.setType("lookaround", this._viewarea);
+ break;
+ case 112: /* p, switch intersect type */
+ switch(this._scene._vf.pickMode.toLowerCase())
+ {
+ case "idbuf":
+ this._scene._vf.pickMode = "color";
+ break;
+ case "color":
+ this._scene._vf.pickMode = "texCoord";
+ break;
+ case "texcoord":
+ this._scene._vf.pickMode = "box";
+ break;
+ default:
+ this._scene._vf.pickMode = "idBuf";
+ break;
+ }
+ x3dom.debug.logInfo("Switch pickMode to '" + this._scene._vf.pickMode + "'.");
+ break;
+ case 114: /* r, reset view */
+ this._viewarea.resetView();
+ break;
+ case 115: /* s, toggle small feature culling */
+ env._vf.smallFeatureCulling = !env._vf.smallFeatureCulling;
+ x3dom.debug.logInfo("Small feature culling " + (env._vf.smallFeatureCulling ? "on" : "off"));
+ break;
+ case 116: /* t, light view */
+ if (this._nodeBag.lights.length > 0) {
+ this._viewarea.animateTo(this._viewarea.getLightMatrix()[0], this._scene.getViewpoint());
+ }
+ break;
+ case 117: /* u, upright position */
+ this._viewarea.uprightView();
+ break;
+ case 118: /* v, print viewpoint position/orientation */
+ var that = this;
+ (function() {
+ var viewpoint = that._viewarea._scene.getViewpoint();
+ var mat_view = that._viewarea.getViewMatrix().inverse();
+
+ var rotation = new x3dom.fields.Quaternion(0, 0, 1, 0);
+ rotation.setValue(mat_view);
+ var rot = rotation.toAxisAngle();
+ var translation = mat_view.e3();
+
+ x3dom.debug.logInfo('\n&lt;Viewpoint position="' + translation.x.toFixed(5) + ' '
+ + translation.y.toFixed(5) + ' ' + translation.z.toFixed(5) + '" ' +
+ 'orientation="' + rot[0].x.toFixed(5) + ' ' + rot[0].y.toFixed(5) + ' '
+ + rot[0].z.toFixed(5) + ' ' + rot[1].toFixed(5) + '" \n\t' +
+ 'zNear="' + viewpoint.getNear().toFixed(5) + '" ' +
+ 'zFar="' + viewpoint.getFar().toFixed(5) + '" ' +
+ 'description="' + viewpoint._vf.description + '"&gt;' +
+ '&lt;/Viewpoint&gt;');
+ })();
+ break;
+ case 119: /* w, walk mode */
+ nav.setType("walk", this._viewarea);
+ break;
+ case 121: /* y, freefly mode */
+ nav.setType("freefly", this._viewarea);
+ break;
+ default:
+ }
+};
+
+x3dom.X3DDocument.prototype.shutdown = function(ctx)
+{
+ if (!ctx) {
+ return;
+ }
+ ctx.shutdown(this._viewarea);
+};
+
+/*
+ * X3DOM JavaScript Library
+ * http://www.x3dom.org
+ *
+ * (C)2009 Fraunhofer IGD, Darmstadt, Germany
+ * Dual licensed under the MIT and GPL
+ *
+ * Based on code originally provided by
+ * Philip Taylor: http://philip.html5.org
+ */
+
+x3dom.MatrixMixer = function(beginTime, endTime) {
+ if (arguments.length === 0) {
+ this._beginTime = 0;
+ this._endTime = 1;
+ }
+ else {
+ this._beginTime = beginTime;
+ this._endTime = endTime;
+ }
+
+ this._beginMat = x3dom.fields.SFMatrix4f.identity();
+ this._beginInvMat = x3dom.fields.SFMatrix4f.identity();
+ this._beginLogMat = x3dom.fields.SFMatrix4f.identity();
+ this._endMat = x3dom.fields.SFMatrix4f.identity();
+ this._endLogMat = x3dom.fields.SFMatrix4f.identity();
+};
+
+x3dom.MatrixMixer.prototype.calcFraction = function(time) {
+ var fraction = (time - this._beginTime) / (this._endTime - this._beginTime);
+ return (Math.sin((fraction * Math.PI) - (Math.PI / 2)) + 1) / 2.0;
+};
+
+x3dom.MatrixMixer.prototype.setBeginMatrix = function(mat) {
+ this._beginMat.setValues(mat);
+ this._beginInvMat = mat.inverse();
+ this._beginLogMat = x3dom.fields.SFMatrix4f.zeroMatrix(); // mat.log();
+};
+
+x3dom.MatrixMixer.prototype.setEndMatrix = function(mat) {
+ this._endMat.setValues(mat);
+ this._endLogMat = mat.mult(this._beginInvMat).log();
+ this._logDiffMat = this._endLogMat.addScaled(this._beginLogMat, -1);
+};
+
+x3dom.MatrixMixer.prototype.mix = function(time) {
+ var mat = null;
+
+ if (time <= this._beginTime)
+ {
+ mat = x3dom.fields.SFMatrix4f.copy(this._beginLogMat);
+ }
+ else
+ {
+ if (time >= this._endTime)
+ {
+ mat = x3dom.fields.SFMatrix4f.copy(this._endLogMat);
+ }
+ else
+ {
+ var fraction = this.calcFraction(time);
+ mat = this._logDiffMat.multiply(fraction).add(this._beginLogMat);
+ }
+ }
+
+ return mat.exp().mult(this._beginMat);
+};
+
+/*
+ * X3DOM JavaScript Library
+ * http://www.x3dom.org
+ *
+ * (C)2009 Fraunhofer IGD, Darmstadt, Germany
+ * Dual licensed under the MIT and GPL
+ *
+ * Based on code originally provided by
+ * Philip Taylor: http://philip.html5.org
+ */
+
+
+/**
+ * Input types - X3DOM allows either navigation or interaction.
+ * During each frame, only interaction of the current type is being processed, it is not possible to
+ * perform interaction (for instance, selecting or dragging objects) and navigation at the same time
+ */
+x3dom.InputTypes = {
+ NAVIGATION: 1,
+ INTERACTION: 2
+};
+
+
+/**
+* Constructor.
+*
+* @class represents a view area
+* @param {x3dom.X3DDocument} document - the target X3DDocument
+* @param {Object} scene - the scene
+*/
+// ### Viewarea ###
+x3dom.Viewarea = function (document, scene) {
+ this._doc = document; // x3ddocument
+ this._scene = scene; // FIXME: updates ?!
+
+ document._nodeBag.viewarea.push(this);
+
+ /**
+ * picking informations containing
+ * pickingpos, pickNorm, pickObj, firstObj, lastObj, lastClickObj, shadowObjId
+ * @var {Object} _pickingInfo
+ * @memberof x3dom.Viewarea
+ * @instance
+ * @protected
+ */
+ this._pickingInfo = {
+ pickPos: new x3dom.fields.SFVec3f(0, 0, 0),
+ pickNorm: new x3dom.fields.SFVec3f(0, 0, 1),
+ pickObj: null,
+ firstObj: null,
+ lastObj: null,
+ lastClickObj: null,
+ shadowObjectId: -1
+ };
+
+ this._currentInputType = x3dom.InputTypes.NAVIGATION;
+
+ /**
+ * rotation matrix
+ * @var {x3dom.fields.SFMatrix4f} _rotMat
+ * @memberof x3dom.Viewarea
+ * @instance
+ * @protected
+ */
+ this._rotMat = x3dom.fields.SFMatrix4f.identity();
+
+ /**
+ * translation matrix
+ * @var {x3dom.fields.SFMatrix4f} _transMat
+ * @memberof x3dom.Viewarea
+ * @instance
+ * @protected
+ */
+ this._transMat = x3dom.fields.SFMatrix4f.identity();
+
+ /**
+ * movement vector
+ * @var {x3dom.fields.SFVec3f} _movement
+ * @memberof x3dom.Viewarea
+ * @instance
+ * @protected
+ */
+ this._movement = new x3dom.fields.SFVec3f(0, 0, 0);
+
+ /**
+ * flag to signal a needed NavigationMatrixUpdate
+ * @var {Boolean} _needNavigationMatrixUpdate
+ * @memberof x3dom.Viewarea
+ * @instance
+ * @protected
+ */
+ this._needNavigationMatrixUpdate = true;
+
+ /**
+ * time passed since last update
+ * @var {Number} _deltaT
+ * @memberof x3dom.Viewarea
+ * @instance
+ * @protected
+ */
+ this._deltaT = 0;
+
+ this._flyMat = null;
+
+ this._pitch = 0;
+ this._yaw = 0;
+
+ /**
+ * eye position of the view area
+ * @var {x3dom.fields.SFVec3f} _eyePos
+ * @memberof x3dom.Viewarea
+ * @instance
+ * @protected
+ */
+ this._eyePos = new x3dom.fields.SFVec3f(0, 0, 0);
+
+ /**
+ * width of the view area
+ * @var {Number} _width
+ * @memberof x3dom.Viewarea
+ * @instance
+ * @protected
+ */
+ this._width = 400;
+
+ /**
+ * height of the view area
+ * @var {Number} _height
+ * @memberof x3dom.Viewarea
+ * @instance
+ * @protected
+ */
+ this._height = 300;
+
+ this._dx = 0;
+ this._dy = 0;
+ this._lastX = -1;
+ this._lastY = -1;
+ this._pressX = -1;
+ this._pressY = -1;
+ this._lastButton = 0;
+
+ this._points = 0; // old render mode flag (but think of better name!)
+ this._numRenderedNodes = 0;
+
+ this._pick = new x3dom.fields.SFVec3f(0, 0, 0);
+ this._pickNorm = new x3dom.fields.SFVec3f(0, 0, 1);
+
+ this._isAnimating = false;
+ this._isMoving = false;
+ this._lastTS = 0;
+ this._mixer = new x3dom.MatrixMixer();
+
+ this.arc = null;
+};
+
+/**
+ * Method gets called every frame with the current timestamp
+ * @param {Number} timeStamp - current time stamp
+ * @return {Boolean} view area animation state
+ */
+x3dom.Viewarea.prototype.tick = function(timeStamp)
+{
+ var needMixAnim = false;
+ var env = this._scene.getEnvironment();
+
+ if (env._vf.enableARC && this.arc == null)
+ {
+ this.arc = new x3dom.arc.AdaptiveRenderControl(this._scene);
+ }
+
+ if (this._mixer._beginTime > 0)
+ {
+ needMixAnim = true;
+
+ if (timeStamp >= this._mixer._beginTime)
+ {
+ if (timeStamp <= this._mixer._endTime)
+ {
+ var mat = this._mixer.mix(timeStamp);
+
+ this._scene.getViewpoint().setView(mat);
+ }
+ else {
+ this._mixer._beginTime = 0;
+ this._mixer._endTime = 0;
+
+ this._scene.getViewpoint().setView(this._mixer._endMat);
+ }
+ }
+ else {
+ this._mixer._beginTime = 0;
+ this._mixer._endTime = 0;
+
+ this._scene.getViewpoint().setView(this._mixer._beginMat);
+ }
+ }
+
+ var needNavAnim = this.navigateTo(timeStamp);
+ var lastIsAnimating = this._isAnimating;
+
+ this._lastTS = timeStamp;
+ this._isAnimating = (needMixAnim || needNavAnim);
+
+ if (this.arc != null )
+ {
+ this.arc.update(this.isMovingOrAnimating() ? 1 : 0, this._doc._x3dElem.runtime.getFPS());
+ }
+
+ return (this._isAnimating || lastIsAnimating);
+};
+
+/**
+ * Returns moving state of view are
+ * @return {Boolean} moving state of view area
+ */
+x3dom.Viewarea.prototype.isMoving = function()
+{
+ return this._isMoving;
+};
+
+/**
+ * Returns animation state of view area
+ * @return {Boolean} animation state of view area
+ */
+x3dom.Viewarea.prototype.isAnimating = function()
+{
+ return this._isAnimating;
+};
+
+/**
+ * is view area moving or animating
+ * @return {Boolean} view area moving or animating state
+ */
+x3dom.Viewarea.prototype.isMovingOrAnimating = function()
+{
+ return (this._isMoving || this._isAnimating);
+};
+
+/**
+ * triggers view area to move to something by passing the timestamp
+ * returning a flag if the view area needs a navigation animation
+ * @return {Boolean} flag if the view area need a navigation state
+ */
+x3dom.Viewarea.prototype.navigateTo = function(timeStamp)
+{
+ var navi = this._scene.getNavigationInfo();
+ var navType = navi.getType();
+
+ var needNavAnim = (this._currentInputType == x3dom.InputTypes.NAVIGATION) &&
+ ( navType === "game" ||
+ (this._lastButton > 0 &&
+ (navType.indexOf("fly") >= 0 ||
+ navType === "walk" ||
+ navType === "helicopter" ||
+ navType.substr(0, 5) === "looka")) );
+
+ this._deltaT = timeStamp - this._lastTS;
+
+ var removeZeroMargin = function(val, offset) {
+ if (val > 0) {
+ if (val <= offset) {
+ return 0;
+ } else {
+ return val - offset;
+ }
+ } else if (val <= 0) {
+ if (val >= -offset) {
+ return 0;
+ } else {
+ return val + offset;
+ }
+ }
+ };
+
+ // slightly increasing slope function
+ var humanizeDiff = function(scale, diff) {
+ return ((diff < 0) ? -1 : 1 ) * Math.pow(scale * Math.abs(diff), 1.65 /*lower is easier on the novice*/);
+ };
+
+ if (needNavAnim)
+ {
+ var avatarRadius = 0.25;
+ var avatarHeight = 1.6;
+ var avatarKnee = 0.75; // TODO; check max. step size
+
+ if (navi._vf.avatarSize.length > 2) {
+ avatarRadius = navi._vf.avatarSize[0];
+ avatarHeight = navi._vf.avatarSize[1];
+ avatarKnee = navi._vf.avatarSize[2];
+ }
+
+
+
+ // get current view matrix
+ var currViewMat = this.getViewMatrix();
+ var dist = 0;
+
+ // estimate one screen size for motion puposes so navigation behaviour
+ // is less dependent on screen geometry. This makes no sense for very
+ // anisotropic cases, so it should probably be configurable.
+ var screenSize = Math.min(this._width, this._height);
+ var rdeltaX = removeZeroMargin((this._pressX - this._lastX) / screenSize, 0.01);
+ var rdeltaY = removeZeroMargin((this._pressY - this._lastY) / screenSize, 0.01);
+
+ var userXdiff = humanizeDiff(1, rdeltaX);
+ var userYdiff = humanizeDiff(1, rdeltaY);
+
+ // check if forwards or backwards (on right button)
+ var step = (this._lastButton & 2) ? -1 : 1;
+ step *= (this._deltaT * navi._vf.speed);
+
+ // factor in delta time and the nav speed setting
+ var userXstep = this._deltaT * navi._vf.speed * userXdiff;
+ var userYstep = this._deltaT * navi._vf.speed * userYdiff;
+
+ var phi = Math.PI * this._deltaT * userXdiff;
+ var theta = Math.PI * this._deltaT * userYdiff;
+
+ if (this._needNavigationMatrixUpdate === true)
+ {
+ this._needNavigationMatrixUpdate = false;
+
+ // reset examine matrices to identity
+ this._rotMat = x3dom.fields.SFMatrix4f.identity();
+ this._transMat = x3dom.fields.SFMatrix4f.identity();
+ this._movement = new x3dom.fields.SFVec3f(0, 0, 0);
+
+ var angleX = 0;
+ var angleY = Math.asin(currViewMat._02);
+ var C = Math.cos(angleY);
+
+ if (Math.abs(C) > 0.0001) {
+ angleX = Math.atan2(-currViewMat._12 / C, currViewMat._22 / C);
+ }
+
+ // too many inversions here can lead to distortions
+ this._flyMat = currViewMat.inverse();
+
+ this._from = this._flyMat.e3();
+ this._at = this._from.subtract(this._flyMat.e2());
+
+ if (navType === "helicopter")
+ this._at.y = this._from.y;
+
+ //lookat, lookaround
+ if (navType.substr(0, 5) === "looka")
+ {
+ this._up = this._flyMat.e1();
+ }
+ //all other modes
+ else
+ {
+ //initially read up-vector from current orientation and keep it
+ if (typeof this._up == 'undefined')
+ {
+ this._up = this._flyMat.e1();
+ }
+ }
+
+ this._pitch = angleX * 180 / Math.PI;
+ this._yaw = angleY * 180 / Math.PI;
+ this._eyePos = this._from.negate();
+ }
+
+ var tmpAt = null, tmpUp = null, tmpMat = null;
+ var q, temp, fin;
+ var lv, sv, up;
+
+ if (navType === "game")
+ {
+ this._pitch += this._dy;
+ this._yaw += this._dx;
+
+ if (this._pitch >= 89) this._pitch = 89;
+ if (this._pitch <= -89) this._pitch = -89;
+ if (this._yaw >= 360) this._yaw -= 360;
+ if (this._yaw < 0) this._yaw = 360 + this._yaw;
+
+ this._dx = 0;
+ this._dy = 0;
+
+ var xMat = x3dom.fields.SFMatrix4f.rotationX(this._pitch / 180 * Math.PI);
+ var yMat = x3dom.fields.SFMatrix4f.rotationY(this._yaw / 180 * Math.PI);
+
+ var fPos = x3dom.fields.SFMatrix4f.translation(this._eyePos);
+
+ this._flyMat = xMat.mult(yMat).mult(fPos);
+
+ // Finally check floor for terrain following (TODO: optimize!)
+ var flyMat = this._flyMat.inverse();
+
+ var tmpFrom = flyMat.e3();
+ tmpUp = new x3dom.fields.SFVec3f(0, -1, 0);
+
+ tmpAt = tmpFrom.add(tmpUp);
+ tmpUp = flyMat.e0().cross(tmpUp).normalize();
+
+ tmpMat = x3dom.fields.SFMatrix4f.lookAt(tmpFrom, tmpAt, tmpUp);
+ tmpMat = tmpMat.inverse();
+
+ this._scene._nameSpace.doc.ctx.pickValue(this, this._width/2, this._height/2,
+ this._lastButton, tmpMat, this.getProjectionMatrix().mult(tmpMat));
+
+ if (this._pickingInfo.pickObj)
+ {
+ dist = this._pickingInfo.pickPos.subtract(tmpFrom).length();
+ //x3dom.debug.logWarning("Floor collision at dist=" + dist.toFixed(4));
+
+ tmpFrom.y += (avatarHeight - dist);
+ flyMat.setTranslate(tmpFrom);
+
+ this._eyePos = flyMat.e3().negate();
+ this._flyMat = flyMat.inverse();
+
+ this._pickingInfo.pickObj = null;
+ }
+
+ this._scene.getViewpoint().setView(this._flyMat);
+
+ return needNavAnim;
+ } // game
+ else if (navType === "helicopter") {
+ var typeParams = navi.getTypeParams();
+
+
+
+ if (this._lastButton & 2) // up/down levelling
+ {
+ var stepUp = 200 * userYstep;
+ typeParams[1] += stepUp;
+ navi.setTypeParams(typeParams);
+ }
+
+ if (this._lastButton & 1) { // forward/backward motion
+ step = 300 * userYstep;
+ }
+ else {
+ step = 0;
+ }
+
+ theta = typeParams[0];
+ this._from.y = typeParams[1];
+ this._at.y = this._from.y;
+
+ // rotate around the up vector
+ q = x3dom.fields.Quaternion.axisAngle(this._up, phi);
+ temp = q.toMatrix();
+
+ fin = x3dom.fields.SFMatrix4f.translation(this._from);
+ fin = fin.mult(temp);
+
+ temp = x3dom.fields.SFMatrix4f.translation(this._from.negate());
+ fin = fin.mult(temp);
+
+ this._at = fin.multMatrixPnt(this._at);
+
+ // rotate around the side vector
+ lv = this._at.subtract(this._from).normalize();
+ sv = lv.cross(this._up).normalize();
+ up = sv.cross(lv).normalize();
+
+ lv = lv.multiply(step);
+
+ this._from = this._from.add(lv);
+ this._at = this._at.add(lv);
+
+ // rotate around the side vector
+ q = x3dom.fields.Quaternion.axisAngle(sv, theta);
+ temp = q.toMatrix();
+
+ fin = x3dom.fields.SFMatrix4f.translation(this._from);
+ fin = fin.mult(temp);
+
+ temp = x3dom.fields.SFMatrix4f.translation(this._from.negate());
+ fin = fin.mult(temp);
+
+ var at = fin.multMatrixPnt(this._at);
+
+ this._flyMat = x3dom.fields.SFMatrix4f.lookAt(this._from, at, up);
+
+ this._scene.getViewpoint().setView(this._flyMat.inverse());
+
+ return needNavAnim;
+ } // helicopter
+
+ // rotate around the up vector
+ q = x3dom.fields.Quaternion.axisAngle(this._up, phi);
+ temp = q.toMatrix();
+
+ fin = x3dom.fields.SFMatrix4f.translation(this._from);
+ fin = fin.mult(temp);
+
+ temp = x3dom.fields.SFMatrix4f.translation(this._from.negate());
+ fin = fin.mult(temp);
+
+ this._at = fin.multMatrixPnt(this._at);
+
+ // rotate around the side vector
+ lv = this._at.subtract(this._from).normalize();
+ sv = lv.cross(this._up).normalize();
+ up = sv.cross(lv).normalize();
+ //this._up = up;
+
+ q = x3dom.fields.Quaternion.axisAngle(sv, theta);
+ temp = q.toMatrix();
+
+ fin = x3dom.fields.SFMatrix4f.translation(this._from);
+ fin = fin.mult(temp);
+
+ temp = x3dom.fields.SFMatrix4f.translation(this._from.negate());
+ fin = fin.mult(temp);
+
+ this._at = fin.multMatrixPnt(this._at);
+
+ // forward along view vector
+ if (navType.substr(0, 5) !== "looka")
+ {
+ var currProjMat = this.getProjectionMatrix();
+
+ if (navType !== "freefly") {
+ if (step < 0) {
+ // backwards: negate viewing direction
+ tmpMat = new x3dom.fields.SFMatrix4f();
+ tmpMat.setValue(this._last_mat_view.e0(), this._last_mat_view.e1(),
+ this._last_mat_view.e2().negate(), this._last_mat_view.e3());
+
+ this._scene._nameSpace.doc.ctx.pickValue(this, this._width/2, this._height/2,
+ this._lastButton, tmpMat, currProjMat.mult(tmpMat));
+ }
+ else {
+ this._scene._nameSpace.doc.ctx.pickValue(this, this._width/2, this._height/2, this._lastButton);
+ }
+ if (this._pickingInfo.pickObj)
+ {
+ dist = this._pickingInfo.pickPos.subtract(this._from).length();
+
+ if (dist <= avatarRadius) {
+ step = 0;
+ }
+ }
+ }
+
+ lv = this._at.subtract(this._from).normalize().multiply(step);
+
+ this._at = this._at.add(lv);
+ this._from = this._from.add(lv);
+
+ // finally attach to ground when walking
+ if (navType === "walk")
+ {
+ tmpAt = this._from.addScaled(up, -1.0);
+ tmpUp = sv.cross(up.negate()).normalize(); // lv
+
+ tmpMat = x3dom.fields.SFMatrix4f.lookAt(this._from, tmpAt, tmpUp);
+ tmpMat = tmpMat.inverse();
+
+ this._scene._nameSpace.doc.ctx.pickValue(this, this._width/2, this._height/2,
+ this._lastButton, tmpMat, currProjMat.mult(tmpMat));
+
+ if (this._pickingInfo.pickObj)
+ {
+ dist = this._pickingInfo.pickPos.subtract(this._from).length();
+
+ this._at = this._at.add(up.multiply(avatarHeight - dist));
+ this._from = this._from.add(up.multiply(avatarHeight - dist));
+ }
+ }
+ this._pickingInfo.pickObj = null;
+ }
+
+ this._flyMat = x3dom.fields.SFMatrix4f.lookAt(this._from, this._at, up);
+
+ this._scene.getViewpoint().setView(this._flyMat.inverse());
+ }
+
+ return needNavAnim;
+};
+
+x3dom.Viewarea.prototype.moveFwd = function()
+{
+ var navi = this._scene.getNavigationInfo();
+
+ if (navi.getType() === "game")
+ {
+ var avatarRadius = 0.25;
+ var avatarHeight = 1.6;
+
+ if (navi._vf.avatarSize.length > 2) {
+ avatarRadius = navi._vf.avatarSize[0];
+ avatarHeight = navi._vf.avatarSize[1];
+ }
+
+ var speed = 5 * this._deltaT * navi._vf.speed;
+ var yRotRad = (this._yaw / 180 * Math.PI);
+ var xRotRad = (this._pitch / 180 * Math.PI);
+
+ var dist = 0;
+ var fMat = this._flyMat.inverse();
+
+ // check front for collisions
+ this._scene._nameSpace.doc.ctx.pickValue(this, this._width/2, this._height/2, this._lastButton);
+
+ if (this._pickingInfo.pickObj)
+ {
+ dist = this._pickingInfo.pickPos.subtract(fMat.e3()).length();
+
+ if (dist <= 2 * avatarRadius) {
+ //x3dom.debug.logWarning("Collision at dist=" + dist.toFixed(4));
+ }
+ else {
+ this._eyePos.x -= Math.sin(yRotRad) * speed;
+ this._eyePos.z += Math.cos(yRotRad) * speed;
+ this._eyePos.y += Math.sin(xRotRad) * speed;
+ }
+ }
+ }
+};
+
+x3dom.Viewarea.prototype.moveBwd = function()
+{
+ var navi = this._scene.getNavigationInfo();
+
+ if (navi.getType() === "game")
+ {
+ var speed = 5 * this._deltaT * navi._vf.speed;
+ var yRotRad = (this._yaw / 180 * Math.PI);
+ var xRotRad = (this._pitch / 180 * Math.PI);
+
+ this._eyePos.x += Math.sin(yRotRad) * speed;
+ this._eyePos.z -= Math.cos(yRotRad) * speed;
+ this._eyePos.y -= Math.sin(xRotRad) * speed;
+ }
+};
+
+x3dom.Viewarea.prototype.strafeRight = function()
+{
+ var navi = this._scene.getNavigationInfo();
+
+ if (navi.getType() === "game")
+ {
+ var speed = 5 * this._deltaT * navi._vf.speed;
+ var yRotRad = (this._yaw / 180 * Math.PI);
+
+ this._eyePos.x -= Math.cos(yRotRad) * speed;
+ this._eyePos.z -= Math.sin(yRotRad) * speed;
+ }
+};
+
+x3dom.Viewarea.prototype.strafeLeft = function()
+{
+ var navi = this._scene.getNavigationInfo();
+
+ if (navi.getType() === "game")
+ {
+ var speed = 5 * this._deltaT * navi._vf.speed;
+ var yRotRad = (this._yaw / 180 * Math.PI);
+
+ this._eyePos.x += Math.cos(yRotRad) * speed;
+ this._eyePos.z += Math.sin(yRotRad) * speed;
+ }
+};
+
+x3dom.Viewarea.prototype.animateTo = function(target, prev, dur)
+{
+ var navi = this._scene.getNavigationInfo();
+
+ if (x3dom.isa(target, x3dom.nodeTypes.X3DViewpointNode)) {
+ target = target.getViewMatrix().mult(target.getCurrentTransform().inverse());
+ }
+
+ if (navi._vf.transitionType[0].toLowerCase() !== "teleport" && navi.getType() !== "game")
+ {
+ if (prev && x3dom.isa(prev, x3dom.nodeTypes.X3DViewpointNode)) {
+ prev = prev.getViewMatrix().mult(prev.getCurrentTransform().inverse()).
+ mult(this._transMat).mult(this._rotMat);
+
+ this._mixer._beginTime = this._lastTS;
+
+ if (arguments.length >= 3) {
+ // for lookAt to assure travel speed of 1 m/s
+ this._mixer._endTime = this._lastTS + dur;
+ }
+ else {
+ this._mixer._endTime = this._lastTS + navi._vf.transitionTime;
+ }
+
+ this._mixer.setBeginMatrix (prev);
+ this._mixer.setEndMatrix (target);
+
+ this._scene.getViewpoint().setView(prev);
+ }
+ else {
+ this._scene.getViewpoint().setView(target);
+ }
+ }
+ else
+ {
+ this._scene.getViewpoint().setView(target);
+ }
+
+ this._rotMat = x3dom.fields.SFMatrix4f.identity();
+ this._transMat = x3dom.fields.SFMatrix4f.identity();
+ this._movement = new x3dom.fields.SFVec3f(0, 0, 0);
+ this._needNavigationMatrixUpdate = true;
+};
+
+x3dom.Viewarea.prototype.getLights = function () {
+ var enabledLights = [];
+ for (var i=0; i<this._doc._nodeBag.lights.length; i++)
+ {
+ if (this._doc._nodeBag.lights[i]._vf.on == true)
+ {
+ enabledLights.push(this._doc._nodeBag.lights[i]);
+ }
+ }
+ return enabledLights;
+};
+
+x3dom.Viewarea.prototype.getLightsShadow = function () {
+ var lights = this._doc._nodeBag.lights;
+ for(var l=0; l<lights.length; l++) {
+ if(lights[l]._vf.shadowIntensity > 0.0){
+ return true;
+ }
+ }
+ return false;
+};
+
+x3dom.Viewarea.prototype.updateSpecialNavigation = function (viewpoint, mat_viewpoint) {
+ var navi = this._scene.getNavigationInfo();
+ var navType = navi.getType();
+
+ // helicopter mode needs to manipulate view matrix specially
+ if (navType == "helicopter" && !navi._heliUpdated)
+ {
+ var typeParams = navi.getTypeParams();
+ var theta = typeParams[0];
+ var currViewMat = viewpoint.getViewMatrix().mult(mat_viewpoint.inverse()).inverse();
+
+ this._from = currViewMat.e3();
+ this._at = this._from.subtract(currViewMat.e2());
+ this._up = new x3dom.fields.SFVec3f(0, 1, 0);
+
+ this._from.y = typeParams[1];
+ this._at.y = this._from.y;
+
+ var sv = currViewMat.e0();
+ var q = x3dom.fields.Quaternion.axisAngle(sv, theta);
+ var temp = q.toMatrix();
+
+ var fin = x3dom.fields.SFMatrix4f.translation(this._from);
+ fin = fin.mult(temp);
+
+ temp = x3dom.fields.SFMatrix4f.translation(this._from.negate());
+ fin = fin.mult(temp);
+
+ this._at = fin.multMatrixPnt(this._at);
+
+ this._flyMat = x3dom.fields.SFMatrix4f.lookAt(this._from, this._at, this._up);
+ this._scene.getViewpoint().setView(this._flyMat.inverse());
+
+ navi._heliUpdated = true;
+ }
+};
+
+/**
+ * Get the view areas view point matrix
+ * @return {x3dom.fields.SFMatrix4f} view areas view point matrix
+ */
+x3dom.Viewarea.prototype.getViewpointMatrix = function ()
+{
+ var viewpoint = this._scene.getViewpoint();
+ var mat_viewpoint = viewpoint.getCurrentTransform();
+
+ this.updateSpecialNavigation(viewpoint, mat_viewpoint);
+
+ return viewpoint.getViewMatrix().mult(mat_viewpoint.inverse());
+};
+
+/**
+ * Get the view areas view matrix
+ * @return {x3dom.fields.SFMatrix4f} view areas view matrix
+ */
+x3dom.Viewarea.prototype.getViewMatrix = function ()
+{
+ return this.getViewpointMatrix().mult(this._transMat).mult(this._rotMat);
+};
+
+x3dom.Viewarea.prototype.getLightMatrix = function ()
+{
+ var lights = this._doc._nodeBag.lights;
+ var i, n = lights.length;
+
+ if (n > 0)
+ {
+ var vol = this._scene.getVolume();
+
+ if (vol.isValid())
+ {
+ var min = x3dom.fields.SFVec3f.MAX();
+ var max = x3dom.fields.SFVec3f.MIN();
+ vol.getBounds(min, max);
+
+ var l_arr = [];
+ var viewpoint = this._scene.getViewpoint();
+ var fov = viewpoint.getFieldOfView();
+
+ var dia = max.subtract(min);
+ var dist1 = (dia.y/2.0) / Math.tan(fov/2.0) + (dia.z/2.0);
+ var dist2 = (dia.x/2.0) / Math.tan(fov/2.0) + (dia.z/2.0);
+
+ dia = min.add(dia.multiply(0.5));
+
+ for (i=0; i<n; i++)
+ {
+ if (x3dom.isa(lights[i], x3dom.nodeTypes.PointLight)) {
+ var wcLoc = lights[i].getCurrentTransform().multMatrixPnt(lights[i]._vf.location);
+ dia = dia.subtract(wcLoc).normalize();
+ }
+ else {
+ var dir = lights[i].getCurrentTransform().multMatrixVec(lights[i]._vf.direction);
+ dir = dir.normalize().negate();
+ dia = dia.add(dir.multiply(1.2 * (dist1 > dist2 ? dist1 : dist2)));
+ }
+
+ l_arr[i] = lights[i].getViewMatrix(dia);
+ }
+
+ return l_arr;
+ }
+ }
+
+ //TODO, this is only for testing
+ return [ this.getViewMatrix() ];
+};
+
+x3dom.Viewarea.prototype.getWCtoLCMatrix = function(lMat)
+{
+ var proj = this.getProjectionMatrix();
+ var view;
+
+ if (arguments.length === 0) {
+ view = this.getLightMatrix()[0];
+ }
+ else {
+ view = lMat;
+ }
+
+ return proj.mult(view);
+};
+
+/**
+ * Get six WCtoLCMatrices for point light
+ * @param {x3dom.fields.SFMatrix4f} view - the view matrix
+ * @param {x3dom.nodeTypes.X3DNode} lightNode - the light node
+ * @param {x3dom.fields.SFMatrix4f} mat_proj - the projection matrix
+ * @return {Array} six WCtoLCMatrices
+ */
+x3dom.Viewarea.prototype.getWCtoLCMatricesPointLight = function(view, lightNode, mat_proj)
+{
+ var zNear = lightNode._vf.zNear;
+ var zFar = lightNode._vf.zFar;
+
+ var proj = this.getLightProjectionMatrix(view, zNear, zFar, false, mat_proj);
+
+ //set projection matrix to 90 degrees FOV (vertical and horizontal)
+ proj._00 = 1;
+ proj._11 = 1;
+
+ var matrices = [];
+
+ //create six matrices to cover all directions of point light
+ matrices[0] = proj.mult(view);
+
+ var rotationMatrix;
+
+ //y-rotation
+ for (var i=1; i<=3; i++){
+ rotationMatrix = x3dom.fields.SFMatrix4f.rotationY(i*Math.PI/2);
+ matrices[i] = proj.mult(rotationMatrix.mult(view));
+ }
+
+ //x-rotation
+ rotationMatrix = x3dom.fields.SFMatrix4f.rotationX(Math.PI/2);
+ matrices[4] = proj.mult(rotationMatrix.mult(view));
+
+ rotationMatrix = x3dom.fields.SFMatrix4f.rotationX(3*Math.PI/2);
+ matrices[5] = proj.mult(rotationMatrix.mult(view));
+
+ return matrices;
+};
+
+/*
+ * Get WCToLCMatrices for cascaded light
+ */
+x3dom.Viewarea.prototype.getWCtoLCMatricesCascaded = function(view, lightNode, mat_proj)
+{
+ var numCascades = Math.max(1, Math.min(lightNode._vf.shadowCascades, 6));
+ var splitFactor = Math.max(0, Math.min(lightNode._vf.shadowSplitFactor, 1));
+ var splitOffset = Math.max(0, Math.min(lightNode._vf.shadowSplitOffset, 1));
+
+ var isSpotLight = x3dom.isa(lightNode, x3dom.nodeTypes.SpotLight);
+ var zNear = lightNode._vf.zNear;
+ var zFar = lightNode._vf.zFar;
+
+ var proj = this.getLightProjectionMatrix(view, zNear, zFar, true, mat_proj);
+
+ if (isSpotLight){
+ //set FOV to 90 degrees
+ proj._00 = 1;
+ proj._11 = 1;
+ }
+
+ //get view projection matrix
+ var viewProj = proj.mult(view);
+
+ var matrices = [];
+
+ if (numCascades == 1){
+ //return if only one cascade
+ matrices[0] = viewProj;
+ return matrices;
+ }
+
+ //compute split positions of view frustum
+ var cascadeSplits = this.getShadowSplitDepths(numCascades, splitFactor, splitOffset, true, mat_proj);
+
+ //calculate fitting matrices and multiply with view projection
+ for (var i=0; i<numCascades; i++){
+ var fittingMat = this.getLightFittingMatrix(viewProj, cascadeSplits[i], cascadeSplits[i+1], mat_proj);
+ matrices[i] = fittingMat.mult(viewProj);
+ }
+
+ return matrices;
+};
+
+x3dom.Viewarea.prototype.getLightProjectionMatrix = function(lMat, zNear, zFar, highPrecision, mat_proj)
+{
+ var proj = x3dom.fields.SFMatrix4f.copy(mat_proj);
+
+ if (!highPrecision || zNear > 0 || zFar > 0) {
+ //replace near and far plane of projection matrix
+ //by values adapted to the light position
+
+ var lightPos = lMat.inverse().e3();
+
+ var nearScale = 0.8;
+ var farScale = 1.2;
+
+ var min = x3dom.fields.SFVec3f.copy(this._scene._lastMin);
+ var max = x3dom.fields.SFVec3f.copy(this._scene._lastMax);
+
+ var dia = max.subtract(min);
+ var sRad = dia.length() / 2;
+
+ var sCenter = min.add(dia.multiply(0.5));
+ var vDist = (lightPos.subtract(sCenter)).length();
+
+ var near, far;
+
+ if (sRad) {
+ if (vDist > sRad)
+ near = (vDist - sRad) * nearScale;
+ else
+ near = 1;
+ far = (vDist + sRad) * farScale;
+ }
+ if (zNear > 0) near = zNear;
+ if (zFar > 0) far = zFar;
+
+ proj._22 = -(far+near)/(far-near);
+ proj._23 = -2.0*far*near / (far-near);
+
+ return proj;
+ }
+ else {
+ //should be more accurate, but also more expensive
+ var cropMatrix = this.getLightCropMatrix(proj.mult(lMat));
+
+ return cropMatrix.mult(proj);
+ }
+};
+
+x3dom.Viewarea.prototype.getProjectionMatrix = function()
+{
+ var viewpoint = this._scene.getViewpoint();
+
+ return viewpoint.getProjectionMatrix(this._width/this._height);
+};
+
+/**
+ * Get the view frustum for a given clipping matrix
+ * @param {x3dom.fields.SFMatrix4f} clipMat - the clipping matrix
+ * @return {x3dom.fields.FrustumVolume} the resulting view frustum
+ */
+x3dom.Viewarea.prototype.getViewfrustum = function(clipMat)
+{
+ var env = this._scene.getEnvironment();
+
+ if (env._vf.frustumCulling == true)
+ {
+ if (arguments.length == 0) {
+ var proj = this.getProjectionMatrix();
+ var view = this.getViewMatrix();
+
+ return new x3dom.fields.FrustumVolume(proj.mult(view));
+ }
+ else {
+ return new x3dom.fields.FrustumVolume(clipMat);
+ }
+ }
+
+ return null;
+};
+
+/**
+ * Get the world coordinates to clipping coordinates matrix by multiplying the projection and view matrices
+ * @return {x3dom.fields.SFMatrix4f} world coordinates to clipping coordinates matrix
+ */
+x3dom.Viewarea.prototype.getWCtoCCMatrix = function()
+{
+ var view = this.getViewMatrix();
+ var proj = this.getProjectionMatrix();
+
+ return proj.mult(view);
+};
+
+/**
+ * Get the clipping coordinates to world coordinates matrix by multiplying the projection and view matrices
+ * @return {x3dom.fields.SFMatrix4f} clipping coordinates to world coordinates matrix
+ */
+x3dom.Viewarea.prototype.getCCtoWCMatrix = function()
+{
+ var mat = this.getWCtoCCMatrix();
+
+ return mat.inverse();
+};
+
+x3dom.Viewarea.prototype.calcViewRay = function(x, y, mat)
+{
+ var cctowc = mat ? mat : this.getCCtoWCMatrix();
+
+ var rx = x / (this._width - 1.0) * 2.0 - 1.0;
+ var ry = (this._height - 1.0 - y) / (this._height - 1.0) * 2.0 - 1.0;
+
+ var from = cctowc.multFullMatrixPnt(new x3dom.fields.SFVec3f(rx, ry, -1));
+ var at = cctowc.multFullMatrixPnt(new x3dom.fields.SFVec3f(rx, ry, 1));
+ var dir = at.subtract(from);
+
+ return new x3dom.fields.Ray(from, dir);
+};
+
+x3dom.Viewarea.prototype.showAll = function(axis)
+{
+ if (axis === undefined)
+ axis = "negZ";
+
+ var scene = this._scene;
+ scene.updateVolume();
+
+ var min = x3dom.fields.SFVec3f.copy(scene._lastMin);
+ var max = x3dom.fields.SFVec3f.copy(scene._lastMax);
+
+ var x = "x", y = "y", z = "z";
+ var sign = 1;
+ var to, from = new x3dom.fields.SFVec3f(0, 0, -1);
+
+ switch (axis) {
+ case "posX":
+ sign = -1;
+ case "negX":
+ z = "x"; x = "y"; y = "z";
+ to = new x3dom.fields.SFVec3f(sign, 0, 0);
+ break;
+ case "posY":
+ sign = -1;
+ case "negY":
+ z = "y"; x = "z"; y = "x";
+ to = new x3dom.fields.SFVec3f(0, sign, 0);
+ break;
+ case "posZ":
+ sign = -1;
+ case "negZ":
+ default:
+ to = new x3dom.fields.SFVec3f(0, 0, -sign);
+ break;
+ }
+
+ var viewpoint = scene.getViewpoint();
+ var fov = viewpoint.getFieldOfView();
+
+ var dia = max.subtract(min);
+
+ var diaz2 = dia[z] / 2.0, tanfov2 = Math.tan(fov / 2.0);
+
+ var dist1 = (dia[y] / 2.0) / tanfov2 + diaz2;
+ var dist2 = (dia[x] / 2.0) / tanfov2 + diaz2;
+
+ dia = min.add(dia.multiply(0.5));
+
+ dia[z] += sign * (dist1 > dist2 ? dist1 : dist2) * 1.01;
+
+ var quat = x3dom.fields.Quaternion.rotateFromTo(from, to);
+
+ var viewmat = quat.toMatrix();
+ viewmat = viewmat.mult(x3dom.fields.SFMatrix4f.translation(dia.negate()));
+
+ this.animateTo(viewmat, viewpoint);
+};
+
+x3dom.Viewarea.prototype.fit = function(min, max, updateCenterOfRotation)
+{
+ if (updateCenterOfRotation === undefined) {
+ updateCenterOfRotation = true;
+ }
+
+ var dia2 = max.subtract(min).multiply(0.5); // half diameter
+ var center = min.add(dia2); // center in wc
+ var bsr = dia2.length(); // bounding sphere radius
+
+ var viewpoint = this._scene.getViewpoint();
+ var fov = viewpoint.getFieldOfView();
+
+ var viewmat = x3dom.fields.SFMatrix4f.copy(this.getViewMatrix());
+
+ var rightDir = new x3dom.fields.SFVec3f(viewmat._00, viewmat._01, viewmat._02);
+ var upDir = new x3dom.fields.SFVec3f(viewmat._10, viewmat._11, viewmat._12);
+ var viewDir = new x3dom.fields.SFVec3f(viewmat._20, viewmat._21, viewmat._22);
+
+ var tanfov2 = Math.tan(fov / 2.0);
+ var dist = bsr / tanfov2;
+
+ var eyePos = center.add(viewDir.multiply(dist));
+
+ viewmat._03 = -rightDir.dot(eyePos);
+ viewmat._13 = -upDir.dot(eyePos);
+ viewmat._23 = -viewDir.dot(eyePos);
+
+ if (updateCenterOfRotation) {
+ viewpoint.setCenterOfRotation(center);
+ }
+
+ if (x3dom.isa(viewpoint, x3dom.nodeTypes.OrthoViewpoint))
+ {
+ viewpoint._vf.fieldOfView[0] = -dist;
+ viewpoint._vf.fieldOfView[1] = -dist;
+ viewpoint._vf.fieldOfView[2] = dist;
+ viewpoint._vf.fieldOfView[3] = dist;
+ viewpoint._projMatrix = null;
+ this.animateTo(viewmat, viewpoint, 0);
+ }
+ else
+ {
+ this.animateTo(viewmat, viewpoint);
+ }
+};
+
+x3dom.Viewarea.prototype.resetView = function()
+{
+ var navi = this._scene.getNavigationInfo();
+
+ if (navi._vf.transitionType[0].toLowerCase() !== "teleport" && navi.getType() !== "game")
+ {
+ this._mixer._beginTime = this._lastTS;
+ this._mixer._endTime = this._lastTS + navi._vf.transitionTime;
+
+ this._mixer.setBeginMatrix(this.getViewMatrix());
+
+ var target = this._scene.getViewpoint();
+ target.resetView();
+
+ target = target.getViewMatrix().mult(target.getCurrentTransform().inverse());
+
+ this._mixer.setEndMatrix(target);
+ }
+ else
+ {
+ this._scene.getViewpoint().resetView();
+ }
+
+ this.resetNavHelpers();
+ navi._heliUpdated = false;
+};
+
+x3dom.Viewarea.prototype.resetNavHelpers = function()
+{
+ this._rotMat = x3dom.fields.SFMatrix4f.identity();
+ this._transMat = x3dom.fields.SFMatrix4f.identity();
+ this._movement = new x3dom.fields.SFVec3f(0, 0, 0);
+ this._needNavigationMatrixUpdate = true;
+};
+
+x3dom.Viewarea.prototype.uprightView = function()
+{
+ var mat = this.getViewMatrix().inverse();
+
+ var from = mat.e3();
+ var at = from.subtract(mat.e2());
+ var up = new x3dom.fields.SFVec3f(0, 1, 0);
+
+ var s = mat.e2().cross(up).normalize();
+ var v = s.cross(up).normalize();
+ at = from.add(v);
+
+ mat = x3dom.fields.SFMatrix4f.lookAt(from, at, up);
+ mat = mat.inverse();
+
+ this.animateTo(mat, this._scene.getViewpoint());
+};
+
+x3dom.Viewarea.prototype.callEvtHandler = function (node, eventType, event)
+{
+ if (!node || !node._xmlNode)
+ return null;
+
+ try {
+ var attrib = node._xmlNode[eventType];
+
+ if (typeof(attrib) === "function") {
+ attrib.call(node._xmlNode, event);
+ }
+ else {
+ var funcStr = node._xmlNode.getAttribute(eventType);
+ var func = new Function('event', funcStr);
+ func.call(node._xmlNode, event);
+ }
+
+ var list = node._listeners[event.type];
+ if (list) {
+ for (var it=0; it<list.length; it++) {
+ list[it].call(node._xmlNode, event);
+ }
+ }
+ }
+ catch(e) {
+ x3dom.debug.logException(e);
+ }
+
+ return event.cancelBubble;
+};
+
+x3dom.Viewarea.prototype.checkEvents = function (obj, x, y, buttonState, eventType)
+{
+ var that = this;
+ var needRecurse = true;
+ var childNode;
+ var i, n;
+ var target = (obj && obj._xmlNode) ? obj._xmlNode : {};
+
+
+ var affectedPointingSensorsList = this._doc._nodeBag.affectedPointingSensors;
+
+
+ var event = {
+ viewarea: that,
+ target: target,
+ type: eventType.substr(2, eventType.length-2),
+ button: buttonState,
+ layerX: x,
+ layerY: y,
+ worldX: that._pick.x,
+ worldY: that._pick.y,
+ worldZ: that._pick.z,
+ normalX: that._pickNorm.x,
+ normalY: that._pickNorm.y,
+ normalZ: that._pickNorm.z,
+ hitPnt: that._pick.toGL(), // for convenience
+ hitObject: target, // deprecated, remove!
+ shadowObjectId: that._pickingInfo.shadowObjectId,
+ cancelBubble: false,
+ stopPropagation: function() { this.cancelBubble = true; },
+ preventDefault: function() { this.cancelBubble = true; }
+ };
+
+ try {
+ var anObj = obj;
+
+ if ( anObj && anObj._xmlNode && anObj._cf.geometry &&
+ !anObj._xmlNode[eventType] &&
+ !anObj._xmlNode.hasAttribute(eventType) &&
+ !anObj._listeners[event.type]) {
+ anObj = anObj._cf.geometry.node;
+ }
+
+ if (anObj && that.callEvtHandler(anObj, eventType, event) === true) {
+ needRecurse = false;
+ }
+ }
+ catch(e) {
+ x3dom.debug.logException(e);
+ }
+
+ var recurse = function(obj) {
+ Array.forEach(obj._parentNodes, function (node) {
+ if ( node._xmlNode && (node._xmlNode[eventType] ||
+ node._xmlNode.hasAttribute(eventType) ||
+ node._listeners[event.type]) )
+ {
+ if (that.callEvtHandler(node, eventType, event) === true) {
+ needRecurse = false;
+ }
+ }
+
+ //find the lowest pointing device sensors in the hierarchy that might be affected
+ //(note that, for X3DTouchSensors, 'affected' does not necessarily mean 'activated')
+ if (buttonState == 0 && affectedPointingSensorsList.length == 0 &&
+ (eventType == 'onmousemove' || eventType == 'onmouseover' || eventType == 'onmouseout') )
+ {
+ n = node._childNodes.length;
+
+ for (i = 0; i < n; ++i)
+ {
+ childNode = node._childNodes[i];
+
+ if (x3dom.isa(childNode, x3dom.nodeTypes.X3DPointingDeviceSensorNode) && childNode._vf.enabled)
+ {
+ affectedPointingSensorsList.push(childNode);
+ }
+ }
+ }
+
+ if (x3dom.isa(node, x3dom.nodeTypes.Anchor) && eventType === 'onclick') {
+ node.handleTouch();
+ needRecurse = false;
+ }
+ else if (needRecurse) {
+ recurse(node);
+ }
+ });
+ };
+
+ if (needRecurse && obj) {
+ recurse(obj);
+ }
+
+ return needRecurse;
+};
+
+
+/**
+ * Notifies all pointing device sensors that are currently affected by mouse events, if any, about the given event
+ * @param {DOMEvent} event - a mouse event, enriched by X3DOM-specific members
+ */
+x3dom.Viewarea.prototype._notifyAffectedPointingSensors = function(event)
+{
+ var funcDict = {
+ "mousedown" : "pointerPressedOverSibling",
+ "mousemove" : "pointerMoved",
+ "mouseover" : "pointerMovedOver",
+ "mouseout" : "pointerMovedOut"
+ };
+
+ var func = funcDict[event.type];
+ var affectedPointingSensorsList = this._doc._nodeBag.affectedPointingSensors;
+ var i, n = affectedPointingSensorsList.length;
+
+ if (n > 0 && func !== undefined)
+ {
+ for (i = 0; i < n; i++)
+ affectedPointingSensorsList[i][func](event);
+ }
+};
+
+
+x3dom.Viewarea.prototype.initMouseState = function()
+{
+ this._deltaT = 0;
+ this._dx = 0;
+ this._dy = 0;
+ this._lastX = -1;
+ this._lastY = -1;
+ this._pressX = -1;
+ this._pressY = -1;
+ this._lastButton = 0;
+ this._isMoving = false;
+ this._needNavigationMatrixUpdate = true;
+};
+
+x3dom.Viewarea.prototype.initTurnTable = function(navi, flyTo)
+{
+ flyTo = (flyTo == undefined) ? true : flyTo;
+
+ var currViewMat = this.getViewMatrix();
+
+ var viewpoint = this._scene.getViewpoint();
+ var center = x3dom.fields.SFVec3f.copy(viewpoint.getCenterOfRotation());
+
+ this._flyMat = currViewMat.inverse();
+
+ this._from = this._flyMat.e3();
+ //this._at = this._from.subtract(this._flyMat.e2());
+ this._at = center;
+ this._up = this._flyMat.e1();
+
+ this._flyMat = x3dom.fields.SFMatrix4f.lookAt(this._from, this._at, this._up);
+ this._flyMat = this.calcOrbit(0, 0, navi);
+
+ var dur = 0.0;
+
+ if (flyTo) {
+ dur = 0.2 / navi._vf.speed; // fly to pivot point
+ }
+
+ this.animateTo(this._flyMat.inverse(), viewpoint, dur);
+
+ this.resetNavHelpers();
+};
+
+x3dom.Viewarea.prototype.onMousePress = function (x, y, buttonState)
+{
+ this._needNavigationMatrixUpdate = true;
+
+ this.prepareEvents(x, y, buttonState, "onmousedown");
+ this._pickingInfo.lastClickObj = this._pickingInfo.pickObj;
+ this._pickingInfo.firstObj = this._pickingInfo.pickObj;
+
+ this._dx = 0;
+ this._dy = 0;
+ this._lastX = x;
+ this._lastY = y;
+ this._pressX = x;
+ this._pressY = y;
+ this._lastButton = buttonState;
+ this._isMoving = false;
+
+ if (this._currentInputType == x3dom.InputTypes.NAVIGATION)
+ {
+ var navi = this._scene.getNavigationInfo();
+
+ if (navi.getType() === "turntable") {
+ this.initTurnTable(navi, false);
+ }
+ }
+};
+
+x3dom.Viewarea.prototype.onMouseRelease = function (x, y, buttonState, prevButton)
+{
+ var i;
+ //if the mouse is released, reset the list of currently affected pointing sensors
+ var affectedPointingSensorsList = this._doc._nodeBag.affectedPointingSensors;
+ for (i = 0; i < affectedPointingSensorsList.length; ++i)
+ {
+ affectedPointingSensorsList[i].pointerReleased();
+ }
+ this._doc._nodeBag.affectedPointingSensors = [];
+
+ var tDist = 3.0; // distance modifier for lookat, could be param
+ var dir;
+ var navi = this._scene.getNavigationInfo();
+ var navType = navi.getType();
+
+ if (this._scene._vf.pickMode.toLowerCase() !== "box") {
+ this.prepareEvents(x, y, prevButton, "onmouseup");
+
+ // click means that mousedown _and_ mouseup were detected on same element
+ if (this._pickingInfo.pickObj &&
+ this._pickingInfo.pickObj === this._pickingInfo.lastClickObj)
+ {
+ this.prepareEvents(x, y, prevButton, "onclick");
+ }
+ else if (!this._pickingInfo.pickObj && !this._pickingInfo.lastClickObj &&
+ !this._pickingInfo.firstObj) // press and release outside object
+ {
+ var eventType = "backgroundClicked";
+ try {
+ if ( this._scene._xmlNode &&
+ (this._scene._xmlNode["on" + eventType] ||
+ this._scene._xmlNode.hasAttribute("on" + eventType) ||
+ this._scene._listeners[eventType]) ) {
+ var event = {
+ target: this._scene._xmlNode, type: eventType,
+ button: prevButton, layerX: x, layerY: y,
+ cancelBubble: false,
+ stopPropagation: function () { this.cancelBubble = true; },
+ preventDefault: function () { this.cancelBubble = true; }
+ };
+ this._scene.callEvtHandler(("on" + eventType), event);
+ }
+ }
+ catch (e) { x3dom.debug.logException("backgroundClicked: " + e); }
+ }
+ }
+ else {
+ var t0 = new Date().getTime();
+ var line = this.calcViewRay(x, y);
+ var isect = this._scene.doIntersect(line);
+ var obj = line.hitObject;
+
+ if (isect && obj)
+ {
+ this._pick.setValues(line.hitPoint);
+
+ this.checkEvents(obj, x, y, buttonState, "onclick");
+
+ x3dom.debug.logInfo("Hit '" + obj._xmlNode.localName + "/ " +
+ obj._DEF + "' at dist=" + line.dist.toFixed(4));
+ x3dom.debug.logInfo("Ray hit at position " + this._pick);
+ }
+
+ var t1 = new Date().getTime() - t0;
+ x3dom.debug.logInfo("Picking time (box): " + t1 + "ms");
+
+ if (!isect) {
+ dir = this.getViewMatrix().e2().negate();
+ var u = dir.dot(line.pos.negate()) / dir.dot(line.dir);
+ this._pick = line.pos.add(line.dir.multiply(u));
+ //x3dom.debug.logInfo("No hit at position " + this._pick);
+ }
+ }
+ this._pickingInfo.firstObj = null;
+
+ if (this._currentInputType == x3dom.InputTypes.NAVIGATION &&
+ (this._pickingInfo.pickObj || this._pickingInfo.shadowObjectId >= 0) &&
+ navType === "lookat" && this._pressX === x && this._pressY === y)
+ {
+ var step = (this._lastButton & 2) ? -1 : 1;
+ var dist = this._pickingInfo.pickPos.subtract(this._from).length() / tDist;
+
+ var laMat = new x3dom.fields.SFMatrix4f();
+ laMat.setValues(this.getViewMatrix());
+ laMat = laMat.inverse();
+
+ var from = laMat.e3();
+ var at = from.subtract(laMat.e2());
+ var up = laMat.e1();
+
+ dir = this._pickingInfo.pickPos.subtract(from);
+ var len = dir.length();
+ dir = dir.normalize();
+
+ //var newUp = new x3dom.fields.SFVec3f(0, 1, 0);
+ var newAt = from.addScaled(dir, len);
+
+ var s = dir.cross(up).normalize();
+ dir = s.cross(up).normalize();
+
+ if (step < 0) {
+ dist = (0.5 + len + dist) * 2;
+ }
+ var newFrom = newAt.addScaled(dir, dist);
+
+ laMat = x3dom.fields.SFMatrix4f.lookAt(newFrom, newAt, up);
+ laMat = laMat.inverse();
+
+ dist = newFrom.subtract(from).length();
+ var dur = Math.max(0.5, Math.log((1 + dist) / navi._vf.speed));
+
+ this.animateTo(laMat, this._scene.getViewpoint(), dur);
+ }
+
+ this._dx = 0;
+ this._dy = 0;
+ this._lastX = x;
+ this._lastY = y;
+ this._lastButton = buttonState;
+ this._isMoving = false;
+};
+
+x3dom.Viewarea.prototype.onMouseOver = function (x, y, buttonState)
+{
+ this._dx = 0;
+ this._dy = 0;
+ this._lastButton = 0;
+ this._isMoving = false;
+ this._lastX = x;
+ this._lastY = y;
+ this._deltaT = 0;
+};
+
+x3dom.Viewarea.prototype.onMouseOut = function (x, y, buttonState)
+{
+ this._dx = 0;
+ this._dy = 0;
+ this._lastButton = 0;
+ this._isMoving = false;
+ this._lastX = x;
+ this._lastY = y;
+ this._deltaT = 0;
+
+ //if the mouse is moved out of the canvas, reset the list of currently affected pointing sensors
+ //(this behaves similar to a mouse release inside the canvas)
+ var i;
+ var affectedPointingSensorsList = this._doc._nodeBag.affectedPointingSensors;
+ for (i = 0; i < affectedPointingSensorsList.length; ++i)
+ {
+ affectedPointingSensorsList[i].pointerReleased();
+ }
+ this._doc._nodeBag.affectedPointingSensors = [];
+};
+
+x3dom.Viewarea.prototype.onDoubleClick = function (x, y)
+{
+ if (this._doc._x3dElem.hasAttribute('disableDoubleClick') &&
+ this._doc._x3dElem.getAttribute('disableDoubleClick') === 'true') {
+ return;
+ }
+
+ var navi = this._scene.getNavigationInfo();
+
+ if (navi.getType() == "none") {
+ return;
+ }
+
+ var pickMode = this._scene._vf.pickMode.toLowerCase();
+
+ if ((pickMode == "color" || pickMode == "texcoord")) {
+ return;
+ }
+
+ var viewpoint = this._scene.getViewpoint();
+
+ viewpoint.setCenterOfRotation(this._pick);
+ x3dom.debug.logInfo("New center of Rotation: " + this._pick);
+
+ var mat = this.getViewMatrix().inverse();
+
+ var from = mat.e3();
+ var at = this._pick;
+ var up = mat.e1();
+
+ var norm = mat.e0().cross(up).normalize();
+ // get distance between look-at point and viewing plane
+ var dist = norm.dot(this._pick.subtract(from));
+
+ from = at.addScaled(norm, -dist);
+ mat = x3dom.fields.SFMatrix4f.lookAt(from, at, up);
+
+ x3dom.debug.logInfo("New camera position: " + from);
+ this.animateTo(mat.inverse(), viewpoint);
+};
+
+x3dom.Viewarea.prototype.handleMoveEvt = function (x, y, buttonState)
+{
+ //pointing sensors might still be in use, if the mouse has previously been pressed over sensor geometry
+ //(in general, transitions between INTERACTION and NAVIGATION require that the mouse is not pressed)
+ if (buttonState == 0)
+ {
+ this._doc._nodeBag.affectedPointingSensors = [];
+ }
+
+ this.prepareEvents(x, y, buttonState, "onmousemove");
+
+ if (this._pickingInfo.pickObj !== this._pickingInfo.lastObj)
+ {
+ if (this._pickingInfo.lastObj) {
+ var obj = this._pickingInfo.pickObj;
+ this._pickingInfo.pickObj = this._pickingInfo.lastObj;
+
+ // call event for lastObj
+ this.prepareEvents(x, y, buttonState, "onmouseout");
+ this._pickingInfo.pickObj = obj;
+ }
+
+ if (this._pickingInfo.pickObj) {
+ // call event for pickObj
+ this.prepareEvents(x, y, buttonState, "onmouseover");
+ }
+
+ this._pickingInfo.lastObj = this._pickingInfo.pickObj;
+ }
+};
+
+x3dom.Viewarea.prototype.onMove = function (x, y, buttonState)
+{
+ this.handleMoveEvt(x, y, buttonState);
+
+ if (this._lastX < 0 || this._lastY < 0) {
+ this._lastX = x;
+ this._lastY = y;
+ }
+ this._dx = x - this._lastX;
+ this._dy = y - this._lastY;
+ this._lastX = x;
+ this._lastY = y;
+};
+
+// multi-touch version of examine mode, called from X3DCanvas.js
+x3dom.Viewarea.prototype.onMoveView = function (translation, rotation)
+{
+ if (this._currentInputType == x3dom.InputTypes.NAVIGATION)
+ {
+ var navi = this._scene.getNavigationInfo();
+ var viewpoint = this._scene.getViewpoint();
+
+ if (navi.getType() === "examine")
+ {
+ if (translation)
+ {
+ var distance = (this._scene._lastMax.subtract(this._scene._lastMin)).length();
+ distance = ((distance < x3dom.fields.Eps) ? 1 : distance) * navi._vf.speed;
+
+ translation = translation.multiply(distance);
+ this._movement = this._movement.add(translation);
+
+ this._transMat = viewpoint.getViewMatrix().inverse().
+ mult(x3dom.fields.SFMatrix4f.translation(this._movement)).
+ mult(viewpoint.getViewMatrix());
+ }
+
+ if (rotation)
+ {
+ var center = viewpoint.getCenterOfRotation();
+ var mat = this.getViewMatrix();
+ mat.setTranslate(new x3dom.fields.SFVec3f(0,0,0));
+
+ this._rotMat = this._rotMat.
+ mult(x3dom.fields.SFMatrix4f.translation(center)).
+ mult(mat.inverse()).mult(rotation).mult(mat).
+ mult(x3dom.fields.SFMatrix4f.translation(center.negate()));
+ }
+
+ this._isMoving = true;
+ }
+ }
+};
+
+x3dom.Viewarea.prototype.onDrag = function (x, y, buttonState)
+{
+ // should onmouseover/-out be handled on drag?
+ this.handleMoveEvt(x, y, buttonState);
+
+ if (this._currentInputType == x3dom.InputTypes.NAVIGATION)
+ {
+ var navi = this._scene.getNavigationInfo();
+
+ var navType = navi.getType();
+ var navRestrict = navi.getExplorationMode();
+
+ if (navType === "none" || navRestrict == 0) {
+ return;
+ }
+
+ var viewpoint = this._scene.getViewpoint();
+
+ var dx = x - this._lastX;
+ var dy = y - this._lastY;
+ var d, vec, cor, mat = null;
+ var alpha, beta;
+
+ buttonState = (!navRestrict || (navRestrict != 7 && buttonState == 1)) ? navRestrict : buttonState;
+
+ if (navType === "examine")
+ {
+ if (buttonState & 1) //left
+ {
+ alpha = (dy * 2 * Math.PI) / this._width;
+ beta = (dx * 2 * Math.PI) / this._height;
+ mat = this.getViewMatrix();
+
+ var mx = x3dom.fields.SFMatrix4f.rotationX(alpha);
+ var my = x3dom.fields.SFMatrix4f.rotationY(beta);
+
+ var center = viewpoint.getCenterOfRotation();
+ mat.setTranslate(new x3dom.fields.SFVec3f(0,0,0));
+
+ this._rotMat = this._rotMat.
+ mult(x3dom.fields.SFMatrix4f.translation(center)).
+ mult(mat.inverse()).mult(mx).mult(my).mult(mat).
+ mult(x3dom.fields.SFMatrix4f.translation(center.negate()));
+ }
+ if (buttonState & 4) //middle
+ {
+ d = (this._scene._lastMax.subtract(this._scene._lastMin)).length();
+ d = ((d < x3dom.fields.Eps) ? 1 : d) * navi._vf.speed;
+
+ vec = new x3dom.fields.SFVec3f(d*dx/this._width, d*(-dy)/this._height, 0);
+ this._movement = this._movement.add(vec);
+
+ mat = this.getViewpointMatrix().mult(this._transMat);
+ //TODO; move real distance along viewing plane
+ this._transMat = mat.inverse().
+ mult(x3dom.fields.SFMatrix4f.translation(this._movement)).
+ mult(mat);
+ }
+ if (buttonState & 2) //right
+ {
+ d = (this._scene._lastMax.subtract(this._scene._lastMin)).length();
+ d = ((d < x3dom.fields.Eps) ? 1 : d) * navi._vf.speed;
+
+ vec = new x3dom.fields.SFVec3f(0, 0, d*(dx+dy)/this._height);
+
+ if (x3dom.isa(viewpoint, x3dom.nodeTypes.OrthoViewpoint))
+ {
+ viewpoint._vf.fieldOfView[0] += vec.z;
+ viewpoint._vf.fieldOfView[1] += vec.z;
+ viewpoint._vf.fieldOfView[2] -= vec.z;
+ viewpoint._vf.fieldOfView[3] -= vec.z;
+ viewpoint._projMatrix = null;
+ }
+ else
+ {
+ this._movement = this._movement.add(vec);
+ mat = this.getViewpointMatrix().mult(this._transMat);
+ //TODO; move real distance along viewing ray
+ this._transMat = mat.inverse().
+ mult(x3dom.fields.SFMatrix4f.translation(this._movement)).
+ mult(mat);
+ }
+ }
+
+ this._isMoving = true;
+ }
+ else if (navType === "turntable") // requires that y is up vector in world coords
+ {
+ if (!this._flyMat)
+ this.initTurnTable(navi, false);
+
+ if (buttonState & 1) //left
+ {
+ alpha = (dy * 2 * Math.PI) / this._height;
+ beta = (dx * 2 * Math.PI) / this._width;
+
+ this._flyMat = this.calcOrbit(alpha, beta, navi);
+ viewpoint.setView(this._flyMat.inverse());
+ }
+ else if (buttonState & 2) //right
+ {
+ d = (this._scene._lastMax.subtract(this._scene._lastMin)).length();
+ d = ((d < x3dom.fields.Eps) ? 1 : d) * navi._vf.speed;
+
+ this._up = this._flyMat.e1();
+ this._from = this._flyMat.e3(); // eye
+
+ // zoom in/out
+ cor = viewpoint.getCenterOfRotation();
+
+ var lastDir = cor.subtract(this._from);
+ var lastDirL = lastDir.length();
+ lastDir = lastDir.normalize();
+
+ var zoomAmount = d * (dx + dy) / this._height;
+
+ // FIXME: very experimental HACK to switch between both versions (clamp to CoR and CoR translation)
+ if (navi._vf.typeParams.length >= 5 && navi._vf.typeParams[4] > 0)
+ {
+ // maintain minimum distance (value given in typeParams[4]) to prevent orientation flips
+ var newDist = Math.min(zoomAmount, lastDirL - navi._vf.typeParams[4]);
+
+ // move along viewing ray, scaled with zoom factor
+ this._from = this._from.addScaled(lastDir, newDist);
+ }
+ else
+ {
+ // add z offset to look-at position, alternatively clamp
+ var diff = zoomAmount - lastDirL + 0.01;
+ if (diff >= 0) {
+ cor = cor.addScaled(lastDir, diff);
+ viewpoint.setCenterOfRotation(cor);
+ }
+
+ // move along viewing ray, scaled with zoom factor
+ this._from = this._from.addScaled(lastDir, zoomAmount);
+ }
+
+ // move along viewing ray, scaled with zoom factor
+ this._from = this._from.addScaled(lastDir, zoomAmount);
+
+ // update camera matrix with lookAt() and invert again
+ this._flyMat = x3dom.fields.SFMatrix4f.lookAt(this._from, cor, this._up);
+ viewpoint.setView(this._flyMat.inverse());
+ }
+ else if (buttonState & 4) //middle
+ {
+ d = (this._scene._lastMax.subtract(this._scene._lastMin)).length();
+ d = ((d < x3dom.fields.Eps) ? 1 : d) * navi._vf.speed * 0.75;
+
+ var tx = -d * dx / this._width;
+ var ty = d * dy / this._height;
+
+ this._up = this._flyMat.e1();
+ this._from = this._flyMat.e3(); // eye
+ var s = this._flyMat.e0();
+
+ // add xy offset to camera position for pan
+ this._from = this._from.addScaled(this._up, ty);
+ this._from = this._from.addScaled(s, tx);
+
+ // add xy offset to look-at position
+ cor = viewpoint.getCenterOfRotation();
+ cor = cor.addScaled(this._up, ty);
+ cor = cor.addScaled(s, tx);
+ viewpoint.setCenterOfRotation(cor);
+
+ // update camera matrix with lookAt() and invert
+ this._flyMat = x3dom.fields.SFMatrix4f.lookAt(this._from, cor, this._up);
+ viewpoint.setView(this._flyMat.inverse());
+ }
+
+ this._isMoving = true;
+ }
+ }
+
+ this._dx = dx;
+ this._dy = dy;
+
+ this._lastX = x;
+ this._lastY = y;
+};
+
+x3dom.Viewarea.prototype.calcOrbit = function (alpha, beta, navi)
+{
+ this._up = this._flyMat.e1();
+ this._from = this._flyMat.e3();
+
+ var offset = this._from.subtract(this._at);
+
+ // angle in xz-plane
+ var phi = Math.atan2(offset.x, offset.z);
+
+ // angle from y-axis
+ var theta = Math.atan2(Math.sqrt(offset.x * offset.x + offset.z * offset.z), offset.y);
+
+ phi -= beta;
+ theta -= alpha;
+
+ // clamp theta
+ var typeParams = navi.getTypeParams();
+ theta = Math.max(typeParams[2], Math.min(typeParams[3], theta));
+
+ var radius = offset.length();
+
+ // calc new cam position
+ var rSinPhi = radius * Math.sin(theta);
+
+ offset.x = rSinPhi * Math.sin(phi);
+ offset.y = radius * Math.cos(theta);
+ offset.z = rSinPhi * Math.cos(phi);
+
+ offset = this._at.add(offset);
+
+ // calc new up vector
+ theta -= Math.PI / 2;
+
+ var sinPhi = Math.sin(theta);
+ var cosPhi = Math.cos(theta);
+ var up = new x3dom.fields.SFVec3f(sinPhi * Math.sin(phi), cosPhi, sinPhi * Math.cos(phi));
+
+ if (up.y < 0)
+ up = up.negate();
+
+ return x3dom.fields.SFMatrix4f.lookAt(offset, this._at, up);
+};
+
+x3dom.Viewarea.prototype.prepareEvents = function (x, y, buttonState, eventType)
+{
+ var affectedPointingSensorsList = this._doc._nodeBag.affectedPointingSensors;
+ var pickMode = this._scene._vf.pickMode.toLowerCase();
+ var avoidTraversal = (pickMode.indexOf("idbuf") == 0 ||
+ pickMode == "color" || pickMode == "texcoord");
+
+ var obj = null;
+
+ if (avoidTraversal) {
+ obj = this._pickingInfo.pickObj;
+
+ if (obj) {
+ this._pick.setValues(this._pickingInfo.pickPos);
+ this._pickNorm.setValues(this._pickingInfo.pickNorm);
+
+ this.checkEvents(obj, x, y, buttonState, eventType);
+
+ if (eventType === "onclick") { // debug
+ if (obj._xmlNode)
+ x3dom.debug.logInfo("Hit \"" + obj._xmlNode.localName + "/ " + obj._DEF + "\"");
+ x3dom.debug.logInfo("Ray hit at position " + this._pick);
+ }
+ }
+ }
+
+ //TODO: this is pretty redundant - but from where should we obtain this event object?
+ // this also needs to work if there is no picked object, and independent from "avoidTraversal"?
+
+ // FIXME; avoidTraversal is only to distinguish between the ancient box and the other render-based pick modes,
+ // thus it seems the cleanest thing to just remove the old traversal-based and non-functional box mode.
+ // Concerning background: what about if we unify the onbackgroundClicked event such that there is also
+ // an onbackgroundMoved event etc?
+
+ var event = {
+ viewarea: this,
+ target: {}, // should be hit xml element
+ type: eventType.substr(2, eventType.length-2),
+ button: buttonState,
+ layerX: x,
+ layerY: y,
+ worldX: this._pick.x,
+ worldY: this._pick.y,
+ worldZ: this._pick.z,
+ normalX: this._pickNorm.x,
+ normalY: this._pickNorm.y,
+ normalZ: this._pickNorm.z,
+ hitPnt: this._pick.toGL(), // for convenience
+ hitObject: (obj && obj._xmlNode) ? obj._xmlNode : null,
+ shadowObjectId: this._pickingInfo.shadowObjectId,
+ cancelBubble: false,
+ stopPropagation: function() { this.cancelBubble = true; },
+ preventDefault: function() { this.cancelBubble = true; }
+ };
+
+ //forward event to affected pointing device sensors
+ this._notifyAffectedPointingSensors(event);
+
+ //switch between navigation and interaction
+ if (affectedPointingSensorsList.length > 0)
+ {
+ this._currentInputType = x3dom.InputTypes.INTERACTION;
+ }
+ else
+ {
+ this._currentInputType = x3dom.InputTypes.NAVIGATION;
+ }
+};
+
+
+x3dom.Viewarea.prototype.getRenderMode = function()
+{
+ // this._points == 0 ? TRIANGLES or TRIANGLE_STRIP
+ // this._points == 1 ? gl.POINTS
+ // this._points == 2 ? gl.LINES
+ // TODO: 3 :== surface with additional wireframe render mode
+ return this._points;
+};
+
+
+x3dom.Viewarea.prototype.getShadowedLights = function()
+{
+ var shadowedLights = [];
+ var shadowIndex = 0;
+ var slights = this.getLights();
+ for (var i=0; i<slights.length; i++){
+ if (slights[i]._vf.shadowIntensity > 0.0){
+ shadowedLights[shadowIndex] = slights[i];
+ shadowIndex++;
+ }
+ }
+ return shadowedLights;
+};
+
+
+/**
+ * Calculate view frustum split positions for the given number of cascades
+ * @param {Number} numCascades - the number of cascades
+ * @param {Number} splitFactor - the splitting factor
+ * @param {Number} splitOffset - the offset for the splits
+ * @param {Array} postProject - the post projection something
+ * @param {x3dom.fields.SFMatrix4f} mat_proj - the projection matrix
+ * @return {Array} the post projection something
+ */
+x3dom.Viewarea.prototype.getShadowSplitDepths = function(numCascades, splitFactor, splitOffset, postProject, mat_proj)
+{
+ var logSplit;
+ var practSplit = [];
+
+ var viewPoint = this._scene.getViewpoint();
+
+ var zNear = viewPoint.getNear();
+ var zFar = viewPoint.getFar();
+
+ practSplit[0] = zNear;
+
+ //pseudo near plane for bigger cascades near camera
+ zNear = zNear + splitOffset*(zFar-zNear)/10;
+
+ //calculate split depths according to "practical split scheme"
+ for (var i=1;i<numCascades;i++){
+ logSplit = zNear * Math.pow((zFar / zNear), i / numCascades);
+ practSplit[i] = splitFactor * logSplit + (1 - splitFactor) * (zNear + i / (numCascades * (zNear-zFar)));
+ }
+ practSplit[numCascades] = zFar;
+
+ //return in view coords
+ if (!postProject)
+ return practSplit;
+
+ //return in post projective coords
+ var postProj = [];
+
+ for (var j=0; j<=numCascades; j++){
+ postProj[j] = mat_proj.multFullMatrixPnt(new x3dom.fields.SFVec3f(0,0,-practSplit[j])).z;
+ }
+
+ return postProj;
+};
+
+
+/*
+ * calculate a matrix to enhance the placement of
+ * the near and far planes of the light projection matrix
+*/
+x3dom.Viewarea.prototype.getLightCropMatrix = function(WCToLCMatrix)
+{
+ //get corner points of scene bounds
+ var sceneMin = x3dom.fields.SFVec3f.copy(this._scene._lastMin);
+ var sceneMax = x3dom.fields.SFVec3f.copy(this._scene._lastMax);
+
+ var sceneCorners = [];
+ sceneCorners[0] = new x3dom.fields.SFVec3f(sceneMin.x, sceneMin.y, sceneMin.z);
+ sceneCorners[1] = new x3dom.fields.SFVec3f(sceneMin.x, sceneMin.y, sceneMax.z);
+ sceneCorners[2] = new x3dom.fields.SFVec3f(sceneMin.x, sceneMax.y, sceneMin.z);
+ sceneCorners[3] = new x3dom.fields.SFVec3f(sceneMin.x, sceneMax.y, sceneMax.z);
+ sceneCorners[4] = new x3dom.fields.SFVec3f(sceneMax.x, sceneMin.y, sceneMin.z);
+ sceneCorners[5] = new x3dom.fields.SFVec3f(sceneMax.x, sceneMin.y, sceneMax.z);
+ sceneCorners[6] = new x3dom.fields.SFVec3f(sceneMax.x, sceneMax.y, sceneMin.z);
+ sceneCorners[7] = new x3dom.fields.SFVec3f(sceneMax.x, sceneMax.y, sceneMax.z);
+
+ //transform scene bounds into light space
+ var i;
+ for (i=0; i<8; i++){
+ sceneCorners[i] = WCToLCMatrix.multFullMatrixPnt(sceneCorners[i]);
+ }
+
+ //determine min and max values in light space
+ var minScene = x3dom.fields.SFVec3f.copy(sceneCorners[0]);
+ var maxScene = x3dom.fields.SFVec3f.copy(sceneCorners[0]);
+
+ for (i=1; i<8; i++){
+ minScene.z = Math.min(sceneCorners[i].z, minScene.z);
+ maxScene.z = Math.max(sceneCorners[i].z, maxScene.z);
+ }
+
+ var scaleZ = 2.0 / (maxScene.z - minScene.z);
+ var offsetZ = -(scaleZ * (maxScene.z + minScene.z)) / 2.0;
+
+ //var scaleZ = 1.0 / (maxScene.z - minScene.z);
+ //var offsetZ = -minScene.z * scaleZ;
+
+ var cropMatrix = x3dom.fields.SFMatrix4f.identity();
+
+ cropMatrix._22 = scaleZ;
+ cropMatrix._23 = offsetZ;
+
+ return cropMatrix;
+};
+
+
+/*
+ * Calculate a matrix to fit the given wctolc-matrix to the split boundaries
+ */
+x3dom.Viewarea.prototype.getLightFittingMatrix = function(WCToLCMatrix, zNear, zFar, mat_proj)
+{
+ var mat_view = this.getViewMatrix();
+ var mat_view_proj = mat_proj.mult(mat_view);
+ var mat_view_proj_inverse = mat_view_proj.inverse();
+
+ //define view frustum corner points in post perspective view space
+ var frustumCorners = [];
+ frustumCorners[0] = new x3dom.fields.SFVec3f(-1, -1, zFar);
+ frustumCorners[1] = new x3dom.fields.SFVec3f(-1, -1, zNear);
+ frustumCorners[2] = new x3dom.fields.SFVec3f(-1, 1, zFar);
+ frustumCorners[3] = new x3dom.fields.SFVec3f(-1, 1, zNear);
+ frustumCorners[4] = new x3dom.fields.SFVec3f( 1, -1, zFar);
+ frustumCorners[5] = new x3dom.fields.SFVec3f( 1, -1, zNear);
+ frustumCorners[6] = new x3dom.fields.SFVec3f( 1, 1, zFar);
+ frustumCorners[7] = new x3dom.fields.SFVec3f( 1, 1, zNear);
+
+
+ //transform corner points into post perspective light space
+ var i;
+ for (i=0; i<8; i++){
+ frustumCorners[i] = mat_view_proj_inverse.multFullMatrixPnt(frustumCorners[i]);
+ frustumCorners[i] = WCToLCMatrix.multFullMatrixPnt(frustumCorners[i]);
+ }
+
+ //calculate minimum and maximum values
+ var minFrustum = x3dom.fields.SFVec3f.copy(frustumCorners[0]);
+ var maxFrustum = x3dom.fields.SFVec3f.copy(frustumCorners[0]);
+
+ for (i=1; i<8; i++){
+ minFrustum.x = Math.min(frustumCorners[i].x, minFrustum.x);
+ minFrustum.y = Math.min(frustumCorners[i].y, minFrustum.y);
+ minFrustum.z = Math.min(frustumCorners[i].z, minFrustum.z);
+
+ maxFrustum.x = Math.max(frustumCorners[i].x, maxFrustum.x);
+ maxFrustum.y = Math.max(frustumCorners[i].y, maxFrustum.y);
+ maxFrustum.z = Math.max(frustumCorners[i].z, maxFrustum.z);
+ }
+
+
+ //clip values to box (-1,-1,-1),(1,1,1)
+ function clip(min,max)
+ {
+ var xMin = min.x;
+ var yMin = min.y;
+ var zMin = min.z;
+ var xMax = max.x;
+ var yMax = max.y;
+ var zMax = max.z;
+
+ if (xMin > 1.0 || xMax < -1.0) {
+ xMin = -1.0;
+ xMax = 1.0;
+ } else {
+ xMin = Math.max(xMin,-1.0);
+ xMax = Math.min(xMax, 1.0);
+ }
+
+ if (yMin > 1.0 || yMax < -1.0) {
+ yMin = -1.0;
+ yMax = 1.0;
+ } else {
+ yMin = Math.max(yMin,-1.0);
+ yMax = Math.min(yMax, 1.0);
+ }
+
+ if (zMin > 1.0 || zMax < -1.0){
+ zMin = -1.0;
+ zMax = 1.0;
+ } else {
+ zMin = Math.max(zMin,-1.0);
+ zMax = Math.min(zMax, 1.0);
+ }
+ var minValues = new x3dom.fields.SFVec3f(xMin,yMin,zMin);
+ var maxValues = new x3dom.fields.SFVec3f(xMax,yMax,zMax);
+
+ return new x3dom.fields.BoxVolume(minValues,maxValues);
+ }
+
+ var frustumBB = clip(minFrustum, maxFrustum);
+
+ //define fitting matrix
+ var scaleX = 2.0 / (frustumBB.max.x - frustumBB.min.x);
+ var scaleY = 2.0 / (frustumBB.max.y - frustumBB.min.y);
+ var offsetX = -(scaleX * (frustumBB.max.x + frustumBB.min.x)) / 2.0;
+ var offsetY = -(scaleY * (frustumBB.max.y + frustumBB.min.y)) / 2.0;
+
+ var fittingMatrix = x3dom.fields.SFMatrix4f.identity();
+
+ fittingMatrix._00 = scaleX;
+ fittingMatrix._11 = scaleY;
+ fittingMatrix._03 = offsetX;
+ fittingMatrix._13 = offsetY;
+
+ return fittingMatrix;
+};
+
+/*
+ * X3DOM JavaScript Library
+ * http://www.x3dom.org
+ *
+ * (C)2009 Fraunhofer IGD, Darmstadt, Germany
+ * Dual licensed under the MIT and GPL
+ *
+ * Based on code originally provided by
+ * Philip Taylor: http://philip.html5.org
+ */
+
+
+/** @class x3dom.Mesh
+*/
+x3dom.Mesh = function(parent)
+{
+ this._parent = parent;
+
+ this._vol = new x3dom.fields.BoxVolume();
+
+ this._invalidate = true;
+ this._numFaces = 0;
+ this._numCoords = 0;
+
+ // cp. x3dom.Utils.primTypeDic for type list
+ this._primType = 'TRIANGLES';
+
+ this._positions = [];
+ this._normals = [];
+ this._texCoords = [];
+ this._colors = [];
+ this._indices = [];
+
+ this._positions[0] = [];
+ this._normals[0] = [];
+ this._texCoords[0] = [];
+ this._colors[0] = [];
+ this._indices[0] = [];
+};
+
+x3dom.Mesh.prototype._dynamicFields = {}; // can hold X3DVertexAttributeNodes
+/*x3dom.Mesh.prototype._positions = [];
+x3dom.Mesh.prototype._normals = [];
+x3dom.Mesh.prototype._texCoords = [];
+x3dom.Mesh.prototype._colors = [];
+x3dom.Mesh.prototype._indices = [];*/
+
+x3dom.Mesh.prototype._numPosComponents = 3;
+x3dom.Mesh.prototype._numTexComponents = 2;
+x3dom.Mesh.prototype._numColComponents = 3;
+x3dom.Mesh.prototype._numNormComponents = 3;
+x3dom.Mesh.prototype._lit = true;
+
+x3dom.Mesh.prototype._vol = null;
+x3dom.Mesh.prototype._invalidate = true;
+x3dom.Mesh.prototype._numFaces = 0;
+x3dom.Mesh.prototype._numCoords = 0;
+
+x3dom.Mesh.prototype.setMeshData = function(positions, normals, texCoords, colors, indices)
+{
+ this._positions[0] = positions;
+ this._normals[0] = normals;
+ this._texCoords[0] = texCoords;
+ this._colors[0] = colors;
+ this._indices[0] = indices;
+
+ this._invalidate = true;
+ this._numFaces = this._indices[0].length / 3;
+ this._numCoords = this._positions[0].length / 3;
+};
+
+x3dom.Mesh.prototype.getVolume = function()
+{
+ if (this._invalidate == true && !this._vol.isValid())
+ {
+ var coords = this._positions[0];
+ var n = coords.length;
+
+ if (n > 3)
+ {
+ var initVal = new x3dom.fields.SFVec3f(coords[0],coords[1],coords[2]);
+ this._vol.setBounds(initVal, initVal);
+
+ for (var i=3; i<n; i+=3)
+ {
+ if (this._vol.min.x > coords[i ]) { this._vol.min.x = coords[i ]; }
+ if (this._vol.min.y > coords[i+1]) { this._vol.min.y = coords[i+1]; }
+ if (this._vol.min.z > coords[i+2]) { this._vol.min.z = coords[i+2]; }
+
+ if (this._vol.max.x < coords[i ]) { this._vol.max.x = coords[i ]; }
+ if (this._vol.max.y < coords[i+1]) { this._vol.max.y = coords[i+1]; }
+ if (this._vol.max.z < coords[i+2]) { this._vol.max.z = coords[i+2]; }
+ }
+ this._invalidate = false;
+ }
+ }
+
+ return this._vol;
+};
+
+x3dom.Mesh.prototype.invalidate = function()
+{
+ this._invalidate = true;
+ this._vol.invalidate();
+};
+
+x3dom.Mesh.prototype.isValid = function()
+{
+ return this._vol.isValid();
+};
+
+x3dom.Mesh.prototype.getCenter = function()
+{
+ return this.getVolume().getCenter();
+};
+
+x3dom.Mesh.prototype.getDiameter = function()
+{
+ return this.getVolume().getDiameter();
+};
+
+x3dom.Mesh.prototype.doIntersect = function(line)
+{
+ var vol = this.getVolume();
+ var isect = line.intersect(vol.min, vol.max);
+
+ //TODO: iterate over all faces!
+ if (isect && line.enter < line.dist)
+ {
+ //x3dom.debug.logInfo("Hit \"" + this._parent._xmlNode.localName + "/ " +
+ // this._parent._DEF + "\" at dist=" + line.enter.toFixed(4));
+
+ line.dist = line.enter;
+ line.hitObject = this._parent;
+ line.hitPoint = line.pos.add(line.dir.multiply(line.enter));
+ }
+
+ return isect;
+};
+
+x3dom.Mesh.prototype.calcNormals = function(creaseAngle, ccw)
+{
+ if (ccw === undefined)
+ ccw = true;
+
+ var multInd = this._multiIndIndices && this._multiIndIndices.length;
+ var idxs = multInd ? this._multiIndIndices : this._indices[0];
+ var coords = this._positions[0];
+
+ var vertNormals = [];
+ var vertFaceNormals = [];
+
+ var i, j, m = coords.length;
+ var a, b, n = null;
+
+ var num = (this._posSize !== undefined && this._posSize > m) ? this._posSize / 3 : m / 3;
+ num = 3 * ((num - Math.floor(num) > 0) ? Math.floor(num + 1) : num);
+
+ for (i = 0; i < num; ++i) {
+ vertFaceNormals[i] = [];
+ }
+
+ num = idxs.length;
+
+ for (i = 0; i < num; i += 3) {
+ var ind_i0, ind_i1, ind_i2;
+ var t;
+
+ if (!multInd) {
+ ind_i0 = idxs[i ] * 3;
+ ind_i1 = idxs[i+1] * 3;
+ ind_i2 = idxs[i+2] * 3;
+
+ t = new x3dom.fields.SFVec3f(coords[ind_i1], coords[ind_i1+1], coords[ind_i1+2]);
+ a = new x3dom.fields.SFVec3f(coords[ind_i0], coords[ind_i0+1], coords[ind_i0+2]).subtract(t);
+ b = t.subtract(new x3dom.fields.SFVec3f(coords[ind_i2], coords[ind_i2+1], coords[ind_i2+2]));
+
+ // this is needed a few lines below
+ ind_i0 = i * 3;
+ ind_i1 = (i+1) * 3;
+ ind_i2 = (i+2) * 3;
+ }
+ else {
+ ind_i0 = i * 3;
+ ind_i1 = (i+1) * 3;
+ ind_i2 = (i+2) * 3;
+
+ t = new x3dom.fields.SFVec3f(coords[ind_i1], coords[ind_i1+1], coords[ind_i1+2]);
+ a = new x3dom.fields.SFVec3f(coords[ind_i0], coords[ind_i0+1], coords[ind_i0+2]).subtract(t);
+ b = t.subtract(new x3dom.fields.SFVec3f(coords[ind_i2], coords[ind_i2+1], coords[ind_i2+2]));
+ }
+
+ n = a.cross(b).normalize();
+ if (!ccw)
+ n = n.negate();
+
+ if (creaseAngle <= x3dom.fields.Eps) {
+ vertNormals[ind_i0 ] = vertNormals[ind_i1 ] = vertNormals[ind_i2 ] = n.x;
+ vertNormals[ind_i0+1] = vertNormals[ind_i1+1] = vertNormals[ind_i2+1] = n.y;
+ vertNormals[ind_i0+2] = vertNormals[ind_i1+2] = vertNormals[ind_i2+2] = n.z;
+ }
+ else {
+ vertFaceNormals[idxs[i ]].push(n);
+ vertFaceNormals[idxs[i+1]].push(n);
+ vertFaceNormals[idxs[i+2]].push(n);
+ }
+ }
+
+ // TODO: allow generic creaseAngle
+ if (creaseAngle > x3dom.fields.Eps)
+ {
+ for (i = 0; i < m; i += 3) {
+ var iThird = i / 3;
+ var arr;
+
+ if (!multInd) {
+ arr = vertFaceNormals[iThird];
+ }
+ else {
+ arr = vertFaceNormals[idxs[iThird]];
+ }
+ num = arr.length;
+
+ n = new x3dom.fields.SFVec3f(0, 0, 0);
+
+ for (j = 0; j < num; ++j) {
+ n = n.add(arr[j]);
+ }
+ n = n.normalize();
+
+ vertNormals[i ] = n.x;
+ vertNormals[i+1] = n.y;
+ vertNormals[i+2] = n.z;
+ }
+ }
+
+ this._normals[0] = vertNormals;
+};
+
+/** @param primStride Number of index entries per primitive, for example 3 for TRIANGLES
+ */
+x3dom.Mesh.prototype.splitMesh = function(primStride, checkMultiIndIndices)
+{
+ var pStride;
+ var isMultiInd;
+
+ if (typeof primStride === undefined) {
+ pStride = 3;
+ } else {
+ pStride = primStride;
+ }
+
+ if (typeof checkMultiIndIndices === undefined) {
+ checkMultiIndIndices = false;
+ }
+
+ var MAX = x3dom.Utils.maxIndexableCoords;
+
+ //adapt MAX to match the primitive stride
+ MAX = Math.floor(MAX / pStride) * pStride;
+
+ if (this._positions[0].length / 3 <= MAX && !checkMultiIndIndices) {
+ return;
+ }
+
+ if (checkMultiIndIndices) {
+ isMultiInd = this._multiIndIndices && this._multiIndIndices.length;
+ } else {
+ isMultiInd = false;
+ }
+
+ var positions = this._positions[0];
+ var normals = this._normals[0];
+ var texCoords = this._texCoords[0];
+ var colors = this._colors[0];
+ var indices = isMultiInd ? this._multiIndIndices : this._indices[0];
+
+ var i = 0;
+
+ do
+ {
+ this._positions[i] = [];
+ this._normals[i] = [];
+ this._texCoords[i] = [];
+ this._colors[i] = [];
+ this._indices[i] = [];
+
+ var k = (indices.length - ((i + 1) * MAX) >= 0);
+
+ if (k) {
+ this._indices[i] = indices.slice(i * MAX, (i + 1) * MAX);
+ } else {
+ this._indices[i] = indices.slice(i * MAX);
+ }
+
+ if(!isMultiInd) {
+ if (i) {
+ var m = i * MAX;
+ for (var j=0, l=this._indices[i].length; j<l; j++) {
+ this._indices[i][j] -= m;
+ }
+ }
+ } else {
+ for (var j=0, l=this._indices[i].length; j<l; j++) {
+ this._indices[i][j] = j;
+ }
+ }
+
+ if (k) {
+ this._positions[i] = positions.slice(i * MAX * 3, 3 * (i + 1) * MAX);
+ } else {
+ this._positions[i] = positions.slice(i * MAX * 3);
+ }
+
+ if (normals.length) {
+ if (k) {
+ this._normals[i] = normals.slice(i * MAX * 3, 3 * (i + 1) * MAX);
+ } else {
+ this._normals[i] = normals.slice(i * MAX * 3);
+ }
+ }
+ if (texCoords.length) {
+ if (k) {
+ this._texCoords[i] = texCoords.slice(i * MAX * this._numTexComponents,
+ this._numTexComponents * (i + 1) * MAX);
+ } else {
+ this._texCoords[i] = texCoords.slice(i * MAX * this._numTexComponents);
+ }
+ }
+ if (colors.length) {
+ if (k) {
+ this._colors[i] = colors.slice(i * MAX * this._numColComponents,
+ this._numColComponents * (i + 1) * MAX);
+ } else {
+ this._colors[i] = colors.slice(i * MAX * this._numColComponents);
+ }
+ }
+ }
+ while (positions.length > ++i * MAX * 3);
+};
+
+x3dom.Mesh.prototype.calcTexCoords = function(mode)
+{
+ this._texCoords[0] = [];
+
+ // TODO; impl. all modes that aren't handled in shader!
+ // FIXME; WebKit requires valid texCoords for texturing
+ if (mode.toLowerCase() === "sphere-local")
+ {
+ for (var i=0, j=0, n=this._normals[0].length; i<n; i+=3)
+ {
+ this._texCoords[0][j++] = 0.5 + this._normals[0][i ] / 2.0;
+ this._texCoords[0][j++] = 0.5 + this._normals[0][i+1] / 2.0;
+ }
+ }
+ else // "plane" is x3d default mapping
+ {
+ var min = new x3dom.fields.SFVec3f(0, 0, 0),
+ max = new x3dom.fields.SFVec3f(0, 0, 0);
+ var vol = this.getVolume();
+
+ vol.getBounds(min, max);
+ var dia = max.subtract(min);
+
+ var S = 0, T = 1;
+
+ if (dia.x >= dia.y)
+ {
+ if (dia.x >= dia.z)
+ {
+ S = 0;
+ T = dia.y >= dia.z ? 1 : 2;
+ }
+ else // dia.x < dia.z
+ {
+ S = 2;
+ T = 0;
+ }
+ }
+ else // dia.x < dia.y
+ {
+ if (dia.y >= dia.z)
+ {
+ S = 1;
+ T = dia.x >= dia.z ? 0 : 2;
+ }
+ else // dia.y < dia.z
+ {
+ S = 2;
+ T = 1;
+ }
+ }
+
+ var sDenom = 1, tDenom = 1;
+ var sMin = 0, tMin = 0;
+
+ switch(S) {
+ case 0: sDenom = dia.x; sMin = min.x; break;
+ case 1: sDenom = dia.y; sMin = min.y; break;
+ case 2: sDenom = dia.z; sMin = min.z; break;
+ }
+
+ switch(T) {
+ case 0: tDenom = dia.x; tMin = min.x; break;
+ case 1: tDenom = dia.y; tMin = min.y; break;
+ case 2: tDenom = dia.z; tMin = min.z; break;
+ }
+
+ for (var k=0, l=0, m=this._positions[0].length; k<m; k+=3)
+ {
+ this._texCoords[0][l++] = (this._positions[0][k+S] - sMin) / sDenom;
+ this._texCoords[0][l++] = (this._positions[0][k+T] - tMin) / tDenom;
+ }
+ }
+};
+
+
+/*
+ * X3DOM JavaScript Library
+ * http://www.x3dom.org
+ *
+ * (C)2009 Fraunhofer IGD, Darmstadt, Germany
+ * Dual licensed under the MIT and GPL
+ *
+ * Based on code originally provided by
+ * Philip Taylor: http://philip.html5.org
+ */
+
+
+/** If used as standalone lib, define some basics first. */
+if (typeof x3dom === "undefined")
+{
+ /**
+ * @namespace x3dom
+ */
+ x3dom = {
+ extend: function(f) {
+ function G() {}
+ G.prototype = f.prototype || f;
+ return new G();
+ },
+
+ debug: {
+ logInfo: function(msg) { console.log(msg); },
+ logWarning: function(msg) { console.warn(msg); },
+ logError: function(msg) { console.error(msg); }
+ }
+ };
+
+ if (!Array.map) {
+ Array.map = function(array, fun, thisp) {
+ var len = array.length;
+ var res = [];
+ for (var i = 0; i < len; i++) {
+ if (i in array) {
+ res[i] = fun.call(thisp, array[i], i, array);
+ }
+ }
+ return res;
+ };
+ }
+
+ console.log("Using x3dom fields.js as standalone math and/or base types library.");
+}
+
+
+/**
+ * The x3dom.fields namespace.
+ * @namespace x3dom.fields
+ */
+x3dom.fields = {};
+
+/** shortcut for convenience and speedup */
+var VecMath = x3dom.fields;
+
+/** Epsilon */
+x3dom.fields.Eps = 0.000001;
+
+
+///////////////////////////////////////////////////////////////////////////////
+// Single-Field Definitions
+///////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////
+/**
+ * Constructor. You must either specify all argument values or no argument. In the latter case, an identity matrix will be created.
+ *
+ * @class Represents a 4x4 matrix in row major format.
+ * @param {Number} [_00=1] - value at [0,0]
+ * @param {Number} [_01=0] - value at [0,1]
+ * @param {Number} [_02=0] - value at [0,2]
+ * @param {Number} [_03=0] - value at [0,3]
+ * @param {Number} [_10=0] - value at [1,0]
+ * @param {Number} [_11=1] - value at [1,1]
+ * @param {Number} [_12=0] - value at [1,2]
+ * @param {Number} [_13=0] - value at [1,3]
+ * @param {Number} [_20=0] - value at [2,0]
+ * @param {Number} [_21=0] - value at [2,1]
+ * @param {Number} [_22=1] - value at [2,2]
+ * @param {Number} [_23=0] - value at [2,3]
+ * @param {Number} [_30=0] - value at [3,0]
+ * @param {Number} [_31=0] - value at [3,1]
+ * @param {Number} [_32=0] - value at [3,2]
+ * @param {Number} [_33=1] - value at [3,3]
+ */
+x3dom.fields.SFMatrix4f = function( _00, _01, _02, _03,
+ _10, _11, _12, _13,
+ _20, _21, _22, _23,
+ _30, _31, _32, _33)
+{
+ if (arguments.length === 0) {
+ this._00 = 1; this._01 = 0; this._02 = 0; this._03 = 0;
+ this._10 = 0; this._11 = 1; this._12 = 0; this._13 = 0;
+ this._20 = 0; this._21 = 0; this._22 = 1; this._23 = 0;
+ this._30 = 0; this._31 = 0; this._32 = 0; this._33 = 1;
+ }
+ else {
+ this._00 = _00; this._01 = _01; this._02 = _02; this._03 = _03;
+ this._10 = _10; this._11 = _11; this._12 = _12; this._13 = _13;
+ this._20 = _20; this._21 = _21; this._22 = _22; this._23 = _23;
+ this._30 = _30; this._31 = _31; this._32 = _32; this._33 = _33;
+ }
+};
+
+/**
+ * Returns the first column vector of the matrix.
+ * @returns {x3dom.fields.SFVec3f} the vector
+ */
+x3dom.fields.SFMatrix4f.prototype.e0 = function () {
+ var baseVec = new x3dom.fields.SFVec3f(this._00, this._10, this._20);
+ return baseVec.normalize();
+};
+
+/**
+ * Returns the second column vector of the matrix.
+ * @returns {x3dom.fields.SFVec3f} the vector
+ */
+x3dom.fields.SFMatrix4f.prototype.e1 = function () {
+ var baseVec = new x3dom.fields.SFVec3f(this._01, this._11, this._21);
+ return baseVec.normalize();
+};
+
+/**
+ * Returns the third column vector of the matrix.
+ * @returns {x3dom.fields.SFVec3f} the vector
+ */
+x3dom.fields.SFMatrix4f.prototype.e2 = function () {
+ var baseVec = new x3dom.fields.SFVec3f(this._02, this._12, this._22);
+ return baseVec.normalize();
+};
+
+/**
+ * Returns the fourth column vector of the matrix.
+ * @returns {x3dom.fields.SFVec3f} the vector
+ */
+x3dom.fields.SFMatrix4f.prototype.e3 = function () {
+ return new x3dom.fields.SFVec3f(this._03, this._13, this._23);
+};
+
+/**
+ * Returns a copy of the argument matrix.
+ * @param {x3dom.fields.SFMatrix4f} that - the matrix to copy
+ * @returns {x3dom.fields.SFMatrix4f} the copy
+ */
+x3dom.fields.SFMatrix4f.copy = function(that) {
+ return new x3dom.fields.SFMatrix4f(
+ that._00, that._01, that._02, that._03,
+ that._10, that._11, that._12, that._13,
+ that._20, that._21, that._22, that._23,
+ that._30, that._31, that._32, that._33
+ );
+};
+
+/**
+ * Returns a copy of the matrix.
+ * @returns {x3dom.fields.SFMatrix4f} the copy
+ */
+x3dom.fields.SFMatrix4f.prototype.copy = function() {
+ return x3dom.fields.SFMatrix4f.copy(this);
+};
+
+/**
+ * Returns a SFMatrix4f identity matrix.
+ * @returns {x3dom.fields.SFMatrix4f} the new identity matrix
+ */
+x3dom.fields.SFMatrix4f.identity = function () {
+ return new x3dom.fields.SFMatrix4f(
+ 1, 0, 0, 0,
+ 0, 1, 0, 0,
+ 0, 0, 1, 0,
+ 0, 0, 0, 1
+ );
+};
+
+/**
+ * Returns a new null matrix.
+ * @returns {x3dom.fields.SFMatrix4f} the new null matrix
+ */
+x3dom.fields.SFMatrix4f.zeroMatrix = function () {
+ return new x3dom.fields.SFMatrix4f(
+ 0, 0, 0, 0,
+ 0, 0, 0, 0,
+ 0, 0, 0, 0,
+ 0, 0, 0, 0
+ );
+};
+
+/**
+ * Returns a new translation matrix.
+ * @param {x3dom.fields.SFVec3f} vec - vector that describes the desired translation
+ * @returns {x3dom.fields.SFMatrix4f} the new identity matrix
+ */
+x3dom.fields.SFMatrix4f.translation = function (vec) {
+ return new x3dom.fields.SFMatrix4f(
+ 1, 0, 0, vec.x,
+ 0, 1, 0, vec.y,
+ 0, 0, 1, vec.z,
+ 0, 0, 0, 1
+ );
+};
+
+/**
+ * Returns a new rotation matrix , rotating around the x axis.
+ * @param {x3dom.fields.SFVec3f} a - angle in radians
+ * @returns {x3dom.fields.SFMatrix4f} the new rotation matrix
+ */
+x3dom.fields.SFMatrix4f.rotationX = function (a) {
+ var c = Math.cos(a);
+ var s = Math.sin(a);
+ return new x3dom.fields.SFMatrix4f(
+ 1, 0, 0, 0,
+ 0, c, -s, 0,
+ 0, s, c, 0,
+ 0, 0, 0, 1
+ );
+};
+
+/**
+ * Returns a new rotation matrix , rotating around the y axis.
+ * @param {x3dom.fields.SFVec3f} a - angle in radians
+ * @returns {x3dom.fields.SFMatrix4f} the new rotation matrix
+ */
+x3dom.fields.SFMatrix4f.rotationY = function (a) {
+ var c = Math.cos(a);
+ var s = Math.sin(a);
+ return new x3dom.fields.SFMatrix4f(
+ c, 0, s, 0,
+ 0, 1, 0, 0,
+ -s, 0, c, 0,
+ 0, 0, 0, 1
+ );
+};
+
+/**
+ * Returns a new rotation matrix , rotating around the z axis.
+ * @param {x3dom.fields.SFVec3f} a - angle in radians
+ * @returns {x3dom.fields.SFMatrix4f} the new rotation matrix
+ */
+x3dom.fields.SFMatrix4f.rotationZ = function (a) {
+ var c = Math.cos(a);
+ var s = Math.sin(a);
+ return new x3dom.fields.SFMatrix4f(
+ c, -s, 0, 0,
+ s, c, 0, 0,
+ 0, 0, 1, 0,
+ 0, 0, 0, 1
+ );
+};
+
+/**
+ * Returns a new scale matrix.
+ * @param {x3dom.fields.SFVec3f} vec - vector containing scale factors along the three main axes
+ * @returns {x3dom.fields.SFMatrix4f} the new scale matrix
+ */
+x3dom.fields.SFMatrix4f.scale = function (vec) {
+ return new x3dom.fields.SFMatrix4f(
+ vec.x, 0, 0, 0,
+ 0, vec.y, 0, 0,
+ 0, 0, vec.z, 0,
+ 0, 0, 0, 1
+ );
+};
+
+/**
+ * Returns a new camera matrix, using the given "look at" parameters.
+ * @param {x3dom.fields.SFVec3f} from - eye point
+ * @param {x3dom.fields.SFVec3f} at - focus ("look at") point
+ * @param {x3dom.fields.SFVec3f} up - up vector
+ * @returns {x3dom.fields.SFMatrix4f} the new camera matrix
+ */
+x3dom.fields.SFMatrix4f.lookAt = function (from, at, up)
+{
+ var view = from.subtract(at).normalize();
+ var right = up.normalize().cross(view).normalize();
+
+ // check if zero vector, i.e. linearly dependent
+ if (right.dot(right) < x3dom.fields.Eps) {
+ x3dom.debug.logWarning("View matrix is linearly dependent.");
+ return x3dom.fields.SFMatrix4f.translation(from);
+ }
+
+ var newUp = view.cross(right).normalize();
+
+ var tmp = x3dom.fields.SFMatrix4f.identity();
+ tmp.setValue(right, newUp, view, from);
+
+ return tmp;
+};
+
+x3dom.fields.SFMatrix4f.perspectiveFrustum = function(left, right, bottom, top, near, far)
+{
+ return new x3dom.fields.SFMatrix4f(
+ 2*near/(right-left), 0, (right+left)/(right-left), 0,
+ 0, 2*near/(top-bottom), (top+bottom)/(top-bottom), 0,
+ 0, 0, -(far+near)/(far-near), -2*far*near/(far-near),
+ 0, 0, -1, 0
+ );
+};
+
+/**
+ * Returns a new perspective projection matrix.
+ * @param {Number} fov - field-of-view angle in radians
+ * @param {Number} aspect - aspect ratio (width / height)
+ * @param {Number} near - near clipping distance
+ * @param {Number} far - far clipping distance
+ * @returns {x3dom.fields.SFMatrix4f} the new projection matrix
+ */
+x3dom.fields.SFMatrix4f.perspective = function(fov, aspect, near, far)
+{
+ var f = 1 / Math.tan(fov / 2);
+
+ return new x3dom.fields.SFMatrix4f(
+ f/aspect, 0, 0, 0,
+ 0, f, 0, 0,
+ 0, 0, (near+far)/(near-far), 2*near*far/(near-far),
+ 0, 0, -1, 0
+ );
+};
+
+/**
+ * Returns a new orthogonal projection matrix.
+ * @param {Number} left - left border value of the view area
+ * @param {Number} right - right border value of the view area
+ * @param {Number} bottom - bottom border value of the view area
+ * @param {Number} top - top border value of the view area
+ * @param {Number} near - near clipping distance
+ * @param {Number} far - far clipping distance
+ * @param {Number} [aspect=1.0] - desired aspect ratio (width / height) of the projected image
+ * @returns {x3dom.fields.SFMatrix4f} the new projection matrix
+ */
+x3dom.fields.SFMatrix4f.ortho = function(left, right, bottom, top, near, far, aspect)
+{
+ var rl = (right - left) / 2; // hs
+ var tb = (top - bottom) / 2; // vs
+ var fn = far - near;
+
+ if (aspect === undefined)
+ aspect = 1.0;
+
+ if (aspect < (rl / tb))
+ tb = rl / aspect;
+ else
+ rl = tb * aspect;
+
+ left = -rl;
+ right = rl;
+ bottom = -tb;
+ top = tb;
+
+ rl *= 2;
+ tb *= 2;
+
+ return new x3dom.fields.SFMatrix4f(
+ 2 / rl, 0, 0, -(right+left) / rl,
+ 0, 2 / tb, 0, -(top+bottom) / tb,
+ 0, 0, -2 / fn, -(far+near) / fn,
+ 0, 0, 0, 1
+ );
+};
+
+/**
+ * Sets the translation components of a homogenous transform matrix.
+ * @param {x3dom.fields.SFVec3f} vec - the translation vector
+ */
+x3dom.fields.SFMatrix4f.prototype.setTranslate = function (vec) {
+ this._03 = vec.x;
+ this._13 = vec.y;
+ this._23 = vec.z;
+};
+
+/**
+ * Sets the scale components of a homogenous transform matrix.
+ * @param {x3dom.fields.SFVec3f} vec - vector containing scale factors along the three main axes
+ */
+x3dom.fields.SFMatrix4f.prototype.setScale = function (vec) {
+ this._00 = vec.x;
+ this._11 = vec.y;
+ this._22 = vec.z;
+};
+
+/**
+ * Sets the rotation components of a homogenous transform matrix.
+ * @param {x3dom.fields.Quaternion} quat - quaternion that describes the rotation
+ */
+x3dom.fields.SFMatrix4f.prototype.setRotate = function (quat) {
+ var xx = quat.x * quat.x;
+ var xy = quat.x * quat.y;
+ var xz = quat.x * quat.z;
+ var yy = quat.y * quat.y;
+ var yz = quat.y * quat.z;
+ var zz = quat.z * quat.z;
+ var wx = quat.w * quat.x;
+ var wy = quat.w * quat.y;
+ var wz = quat.w * quat.z;
+
+ this._00 = 1 - 2 * (yy + zz); this._01 = 2 * (xy - wz); this._02 = 2 * (xz + wy);
+ this._10 = 2 * (xy + wz); this._11 = 1 - 2 * (xx + zz); this._12 = 2 * (yz - wx);
+ this._20 = 2 * (xz - wy); this._21 = 2 * (yz + wx); this._22 = 1 - 2 * (xx + yy);
+};
+
+/**
+ * Creates a new matrix from a column major string representation, with values separated by commas
+ * @param {String} str - string to parse
+ * @return {x3dom.fields.SFMatrix4f} the new matrix
+ */
+x3dom.fields.SFMatrix4f.parseRotation = function (str) {
+ var m = /^([+\-]?\d*\.*\d*[eE]?[+\-]?\d*?)\s*,?\s*([+\-]?\d*\.*\d*[eE]?[+\-]?\d*?)\s*,?\s*([+\-]?\d*\.*\d*[eE]?[+\-]?\d*?)\s*,?\s*([+\-]?\d*\.*\d*[eE]?[+\-]?\d*?)$/.exec(str);
+ var x = +m[1], y = +m[2], z = +m[3], a = +m[4];
+
+ var d = Math.sqrt(x*x + y*y + z*z);
+ if (d === 0) {
+ x = 1; y = z = 0;
+ } else {
+ x /= d; y /= d; z /= d;
+ }
+
+ var c = Math.cos(a);
+ var s = Math.sin(a);
+ var t = 1 - c;
+
+ return new x3dom.fields.SFMatrix4f(
+ t*x*x+c, t*x*y+s*z, t*x*z-s*y, 0,
+ t*x*y-s*z, t*y*y+c, t*y*z+s*x, 0,
+ t*x*z+s*y, t*y*z-s*x, t*z*z+c, 0,
+ 0, 0, 0, 1
+ ).transpose();
+};
+
+/**
+ * Creates a new matrix from a X3D-conformant string representation
+ * @param {String} str - string to parse
+ * @return {x3dom.fields.SFMatrix4f} the new rotation matrix
+ */
+x3dom.fields.SFMatrix4f.parse = function (str) {
+ var needTranspose = false;
+ var val = /matrix.*\((.+)\)/;
+ if (val.exec(str)) {
+ str = RegExp.$1;
+ needTranspose = true;
+ }
+ var arr = Array.map(str.split(/[,\s]+/), function (n) { return +n; });
+ if (arr.length >= 16)
+ {
+ if (!needTranspose) {
+ return new x3dom.fields.SFMatrix4f(
+ arr[0], arr[1], arr[2], arr[3],
+ arr[4], arr[5], arr[6], arr[7],
+ arr[8], arr[9], arr[10], arr[11],
+ arr[12], arr[13], arr[14], arr[15]
+ );
+ }
+ else {
+ return new x3dom.fields.SFMatrix4f(
+ arr[0], arr[4], arr[8], arr[12],
+ arr[1], arr[5], arr[9], arr[13],
+ arr[2], arr[6], arr[10], arr[14],
+ arr[3], arr[7], arr[11], arr[15]
+ );
+ }
+ }
+ else if (arr.length === 6) {
+ return new x3dom.fields.SFMatrix4f(
+ arr[0], arr[1], 0, arr[4],
+ arr[2], arr[3], 0, arr[5],
+ 0, 0, 1, 0,
+ 0, 0, 0, 1
+ );
+ }
+ else {
+ x3dom.debug.logWarning("SFMatrix4f - can't parse string: " + str);
+ return x3dom.fields.SFMatrix4f.identity();
+ }
+};
+
+/**
+ * Returns the result of multiplying this matrix with the given one, using "post-multiplication" / "right multiply".
+ * @param {x3dom.fields.SFMatrix4f} that - matrix to multiply with this one
+ * @return {x3dom.fields.SFMatrix4f} resulting matrix
+ */
+x3dom.fields.SFMatrix4f.prototype.mult = function (that) {
+ return new x3dom.fields.SFMatrix4f(
+ this._00*that._00+this._01*that._10+this._02*that._20+this._03*that._30,
+ this._00*that._01+this._01*that._11+this._02*that._21+this._03*that._31,
+ this._00*that._02+this._01*that._12+this._02*that._22+this._03*that._32,
+ this._00*that._03+this._01*that._13+this._02*that._23+this._03*that._33,
+ this._10*that._00+this._11*that._10+this._12*that._20+this._13*that._30,
+ this._10*that._01+this._11*that._11+this._12*that._21+this._13*that._31,
+ this._10*that._02+this._11*that._12+this._12*that._22+this._13*that._32,
+ this._10*that._03+this._11*that._13+this._12*that._23+this._13*that._33,
+ this._20*that._00+this._21*that._10+this._22*that._20+this._23*that._30,
+ this._20*that._01+this._21*that._11+this._22*that._21+this._23*that._31,
+ this._20*that._02+this._21*that._12+this._22*that._22+this._23*that._32,
+ this._20*that._03+this._21*that._13+this._22*that._23+this._23*that._33,
+ this._30*that._00+this._31*that._10+this._32*that._20+this._33*that._30,
+ this._30*that._01+this._31*that._11+this._32*that._21+this._33*that._31,
+ this._30*that._02+this._31*that._12+this._32*that._22+this._33*that._32,
+ this._30*that._03+this._31*that._13+this._32*that._23+this._33*that._33
+ );
+};
+
+/**
+ * Transforms a given 3D point, using this matrix as a homogenous transform matrix
+ * (ignores projection part of matrix for speedup in standard cases).
+ * @param {x3dom.fields.SFVec3f} vec - point to transform
+ * @return {x3dom.fields.SFVec3f} resulting point
+ */
+x3dom.fields.SFMatrix4f.prototype.multMatrixPnt = function (vec) {
+ return new x3dom.fields.SFVec3f(
+ this._00*vec.x + this._01*vec.y + this._02*vec.z + this._03,
+ this._10*vec.x + this._11*vec.y + this._12*vec.z + this._13,
+ this._20*vec.x + this._21*vec.y + this._22*vec.z + this._23
+ );
+};
+
+/**
+ * Transforms a given 3D vector, using this matrix as a homogenous transform matrix.
+ * @param {x3dom.fields.SFVec3f} vec - vector to transform
+ * @return {x3dom.fields.SFVec3f} resulting vector
+ */
+x3dom.fields.SFMatrix4f.prototype.multMatrixVec = function (vec) {
+ return new x3dom.fields.SFVec3f(
+ this._00*vec.x + this._01*vec.y + this._02*vec.z,
+ this._10*vec.x + this._11*vec.y + this._12*vec.z,
+ this._20*vec.x + this._21*vec.y + this._22*vec.z
+ );
+};
+
+/**
+ * Transforms a given 3D point, using this matrix as a transform matrix
+ * (also includes projection part of matrix - required for e.g. modelview-projection matrix).
+ * The resulting point is normalized by a w component.
+ * @param {x3dom.fields.SFVec3f} vec - point to transform
+ * @return {x3dom.fields.SFVec3f} resulting point
+ */
+x3dom.fields.SFMatrix4f.prototype.multFullMatrixPnt = function (vec) {
+ var w = this._30*vec.x + this._31*vec.y + this._32*vec.z + this._33;
+ if (w) { w = 1.0 / w; }
+ return new x3dom.fields.SFVec3f(
+ (this._00*vec.x + this._01*vec.y + this._02*vec.z + this._03) * w,
+ (this._10*vec.x + this._11*vec.y + this._12*vec.z + this._13) * w,
+ (this._20*vec.x + this._21*vec.y + this._22*vec.z + this._23) * w
+ );
+};
+
+
+/**
+ * Transforms a given 3D point, using this matrix as a transform matrix
+ * (also includes projection part of matrix - required for e.g. modelview-projection matrix).
+ * The resulting point is normalized by a w component.
+ * @param {x3dom.fields.SFVec4f} vec - plane to transform
+ * @return {x3dom.fields.SFVec4f} resulting plane
+ */
+x3dom.fields.SFMatrix4f.prototype.multMatrixPlane = function (plane) {
+
+ var normal = new x3dom.fields.SFVec3f(plane.x, plane.y, plane.z);
+
+ var memberPnt = normal.multiply(-plane.w);
+
+ memberPnt = this.multMatrixPnt(memberPnt);
+
+ var invTranspose = this.inverse().transpose();
+
+ normal = invTranspose.multMatrixVec(normal);
+
+ var d = -normal.dot(memberPnt);
+
+ return new x3dom.fields.SFVec4f(normal.x, normal.y, normal.z, d);
+};
+
+/**
+ * Returns a transposed version of this matrix.
+ * @return {x3dom.fields.SFMatrix4f} resulting matrix
+ */
+x3dom.fields.SFMatrix4f.prototype.transpose = function () {
+ return new x3dom.fields.SFMatrix4f(
+ this._00, this._10, this._20, this._30,
+ this._01, this._11, this._21, this._31,
+ this._02, this._12, this._22, this._32,
+ this._03, this._13, this._23, this._33
+ );
+};
+
+/**
+ * Returns a negated version of this matrix.
+ * @return {x3dom.fields.SFMatrix4f} resulting matrix
+ */
+x3dom.fields.SFMatrix4f.prototype.negate = function () {
+ return new x3dom.fields.SFMatrix4f(
+ -this._00, -this._01, -this._02, -this._03,
+ -this._10, -this._11, -this._12, -this._13,
+ -this._20, -this._21, -this._22, -this._23,
+ -this._30, -this._31, -this._32, -this._33
+ );
+};
+
+/**
+ * Returns a scaled version of this matrix.
+ * @param {Number} s - scale factor
+ * @return {x3dom.fields.SFMatrix4f} resulting matrix
+ */
+x3dom.fields.SFMatrix4f.prototype.multiply = function (s) {
+ return new x3dom.fields.SFMatrix4f(
+ s*this._00, s*this._01, s*this._02, s*this._03,
+ s*this._10, s*this._11, s*this._12, s*this._13,
+ s*this._20, s*this._21, s*this._22, s*this._23,
+ s*this._30, s*this._31, s*this._32, s*this._33
+ );
+};
+
+/**
+ * Returns the result of adding the given matrix to this matrix.
+ * @param {x3dom.fields.SFMatrix4f} that - the other matrix
+ * @return {x3dom.fields.SFMatrix4f} resulting matrix
+ */
+x3dom.fields.SFMatrix4f.prototype.add = function (that) {
+ return new x3dom.fields.SFMatrix4f(
+ this._00+that._00, this._01+that._01, this._02+that._02, this._03+that._03,
+ this._10+that._10, this._11+that._11, this._12+that._12, this._13+that._13,
+ this._20+that._20, this._21+that._21, this._22+that._22, this._23+that._23,
+ this._30+that._30, this._31+that._31, this._32+that._32, this._33+that._33
+ );
+};
+
+/**
+ * Returns the result of adding the given matrix to this matrix, using an additional scale factor for the argument matrix.
+ * @param {x3dom.fields.SFMatrix4f} that - the other matrix
+ * @param {Number} s - the scale factor
+ * @return {x3dom.fields.SFMatrix4f} resulting matrix
+ */
+x3dom.fields.SFMatrix4f.prototype.addScaled = function (that, s) {
+ return new x3dom.fields.SFMatrix4f(
+ this._00+s*that._00, this._01+s*that._01, this._02+s*that._02, this._03+s*that._03,
+ this._10+s*that._10, this._11+s*that._11, this._12+s*that._12, this._13+s*that._13,
+ this._20+s*that._20, this._21+s*that._21, this._22+s*that._22, this._23+s*that._23,
+ this._30+s*that._30, this._31+s*that._31, this._32+s*that._32, this._33+s*that._33
+ );
+};
+
+/**
+ * Fills the values of this matrix with the values of the other one.
+ * @param {x3dom.fields.SFMatrix4f} that - the other matrix
+ */
+x3dom.fields.SFMatrix4f.prototype.setValues = function (that) {
+ this._00 = that._00; this._01 = that._01; this._02 = that._02; this._03 = that._03;
+ this._10 = that._10; this._11 = that._11; this._12 = that._12; this._13 = that._13;
+ this._20 = that._20; this._21 = that._21; this._22 = that._22; this._23 = that._23;
+ this._30 = that._30; this._31 = that._31; this._32 = that._32; this._33 = that._33;
+};
+
+/**
+ * Fills the upper left 3x3 or 3x4 values of this matrix, using the given (three or four) column vectors.
+ * @param {x3dom.fields.SFVec3f} v1 - first column vector
+ * @param {x3dom.fields.SFVec3f} v2 - second column vector
+ * @param {x3dom.fields.SFVec3f} v3 - third column vector
+ * @param {x3dom.fields.SFVec3f} [v4=undefined] - fourth column vector
+ */
+x3dom.fields.SFMatrix4f.prototype.setValue = function (v1, v2, v3, v4) {
+ this._00 = v1.x; this._01 = v2.x; this._02 = v3.x;
+ this._10 = v1.y; this._11 = v2.y; this._12 = v3.y;
+ this._20 = v1.z; this._21 = v2.z; this._22 = v3.z;
+ this._30 = 0; this._31 = 0; this._32 = 0;
+
+ if (arguments.length > 3) {
+ this._03 = v4.x;
+ this._13 = v4.y;
+ this._23 = v4.z;
+ this._33 = 1;
+ }
+};
+
+/**
+ * Fills the values of this matrix, using the given array.
+ * @param {Number[]} a - array, the first 16 values will be used to initialize the matrix
+ */
+x3dom.fields.SFMatrix4f.prototype.setFromArray = function (a) {
+ this._00 = a[0]; this._01 = a[4]; this._02 = a[ 8]; this._03 = a[12];
+ this._10 = a[1]; this._11 = a[5]; this._12 = a[ 9]; this._13 = a[13];
+ this._20 = a[2]; this._21 = a[6]; this._22 = a[10]; this._23 = a[14];
+ this._30 = a[3]; this._31 = a[7]; this._32 = a[11]; this._33 = a[15];
+};
+
+/**
+ * Returns a column major version of this matrix, packed into a single array.
+ * @returns {Number[]} resulting array of 16 values
+ */
+x3dom.fields.SFMatrix4f.prototype.toGL = function () {
+ return [
+ this._00, this._10, this._20, this._30,
+ this._01, this._11, this._21, this._31,
+ this._02, this._12, this._22, this._32,
+ this._03, this._13, this._23, this._33
+ ];
+};
+
+/**
+ * Returns the value of this matrix at a given position.
+ * @param {Number} i - row index (starting with 0)
+ * @param {Number} j - column index (starting with 0)
+ * @returns {Number} the value
+ */
+x3dom.fields.SFMatrix4f.prototype.at = function (i, j) {
+ var field = "_" + i + j;
+ return this[field];
+};
+
+/**
+ * Computes the square root of the matrix, assuming that its determinant is greater than zero.
+ * @return {SFMatrix4f} a matrix containing the result
+ */
+x3dom.fields.SFMatrix4f.prototype.sqrt = function () {
+ var Y = x3dom.fields.SFMatrix4f.identity();
+ var result = x3dom.fields.SFMatrix4f.copy(this);
+
+ for (var i=0; i<6; i++)
+ {
+ var iX = result.inverse();
+ var iY = (i == 0) ? x3dom.fields.SFMatrix4f.identity() : Y.inverse();
+
+ var rd = result.det(), yd = Y.det();
+
+ var g = Math.abs( Math.pow(rd * yd, -0.125) );
+ var ig = 1.0 / g;
+
+ result = result.multiply(g);
+ result = result.addScaled(iY, ig);
+ result = result.multiply(0.5);
+
+ Y = Y.multiply(g);
+ Y = Y.addScaled(iX, ig);
+ Y = Y.multiply(0.5);
+ }
+
+ return result;
+};
+
+/**
+ * Returns the largest absolute value of all entries in the matrix.
+ * This is only a helper for calculating log and not the usual Infinity-norm for matrices.
+ * @returns {Number} the largest absolute value
+ */
+x3dom.fields.SFMatrix4f.prototype.normInfinity = function () {
+ var t = 0, m = 0;
+
+ if ((t = Math.abs(this._00)) > m) {
+ m = t;
+ }
+ if ((t = Math.abs(this._01)) > m) {
+ m = t;
+ }
+ if ((t = Math.abs(this._02)) > m) {
+ m = t;
+ }
+ if ((t = Math.abs(this._03)) > m) {
+ m = t;
+ }
+
+ if ((t = Math.abs(this._10)) > m) {
+ m = t;
+ }
+ if ((t = Math.abs(this._11)) > m) {
+ m = t;
+ }
+ if ((t = Math.abs(this._12)) > m) {
+ m = t;
+ }
+ if ((t = Math.abs(this._13)) > m) {
+ m = t;
+ }
+
+ if ((t = Math.abs(this._20)) > m) {
+ m = t;
+ }
+ if ((t = Math.abs(this._21)) > m) {
+ m = t;
+ }
+ if ((t = Math.abs(this._22)) > m) {
+ m = t;
+ }
+ if ((t = Math.abs(this._23)) > m) {
+ m = t;
+ }
+
+ if ((t = Math.abs(this._30)) > m) {
+ m = t;
+ }
+ if ((t = Math.abs(this._31)) > m) {
+ m = t;
+ }
+ if ((t = Math.abs(this._32)) > m) {
+ m = t;
+ }
+ if ((t = Math.abs(this._33)) > m) {
+ m = t;
+ }
+
+ return m;
+};
+
+/**
+ * Returns the 1-norm of the upper left 3x3 part of this matrix.
+ * The 1-norm is also known as maximum absolute column sum norm.
+ * @returns {Number} the resulting number
+ */
+x3dom.fields.SFMatrix4f.prototype.norm1_3x3 = function() {
+ var max = Math.abs(this._00) +
+ Math.abs(this._10) +
+ Math.abs(this._20);
+ var t = 0;
+
+ if ((t = Math.abs(this._01) +
+ Math.abs(this._11) +
+ Math.abs(this._21)) > max) {
+ max = t;
+ }
+
+ if ((t = Math.abs(this._02) +
+ Math.abs(this._12) +
+ Math.abs(this._22)) > max) {
+ max = t;
+ }
+
+ return max;
+};
+
+/**
+ * Returns the infinity-norm of the upper left 3x3 part of this matrix.
+ * The infinity-norm is also known as maximum absolute row sum norm.
+ * @returns {Number} the resulting number
+ */
+x3dom.fields.SFMatrix4f.prototype.normInf_3x3 = function() {
+ var max = Math.abs(this._00) +
+ Math.abs(this._01) +
+ Math.abs(this._02);
+ var t = 0;
+
+ if ((t = Math.abs(this._10) +
+ Math.abs(this._11) +
+ Math.abs(this._12)) > max) {
+ max = t;
+ }
+
+ if ((t = Math.abs(this._20) +
+ Math.abs(this._21) +
+ Math.abs(this._22)) > max) {
+ max = t;
+ }
+
+ return max;
+};
+
+/**
+ * Computes the transposed adjoint of the upper left 3x3 part of this matrix,
+ * and stores it in the upper left part of a new 4x4 identity matrix.
+ * @returns {x3dom.fields.SFMatrix4f} the resulting matrix
+ */
+x3dom.fields.SFMatrix4f.prototype.adjointT_3x3 = function () {
+ var result = x3dom.fields.SFMatrix4f.identity();
+
+ result._00 = this._11 * this._22 - this._12 * this._21;
+ result._01 = this._12 * this._20 - this._10 * this._22;
+ result._02 = this._10 * this._21 - this._11 * this._20;
+
+ result._10 = this._21 * this._02 - this._22 * this._01;
+ result._11 = this._22 * this._00 - this._20 * this._02;
+ result._12 = this._20 * this._01 - this._21 * this._00;
+
+ result._20 = this._01 * this._12 - this._02 * this._11;
+ result._21 = this._02 * this._10 - this._00 * this._12;
+ result._22 = this._00 * this._11 - this._01 * this._10;
+
+ return result;
+};
+
+/**
+ * Checks whether this matrix equals another matrix.
+ * @param {x3dom.fields.SFMatrix4f} that - the other matrix
+ * @returns {Boolean}
+ */
+x3dom.fields.SFMatrix4f.prototype.equals = function (that) {
+ var eps = 0.000000000001;
+ return Math.abs(this._00-that._00) < eps && Math.abs(this._01-that._01) < eps &&
+ Math.abs(this._02-that._02) < eps && Math.abs(this._03-that._03) < eps &&
+ Math.abs(this._10-that._10) < eps && Math.abs(this._11-that._11) < eps &&
+ Math.abs(this._12-that._12) < eps && Math.abs(this._13-that._13) < eps &&
+ Math.abs(this._20-that._20) < eps && Math.abs(this._21-that._21) < eps &&
+ Math.abs(this._22-that._22) < eps && Math.abs(this._23-that._23) < eps &&
+ Math.abs(this._30-that._30) < eps && Math.abs(this._31-that._31) < eps &&
+ Math.abs(this._32-that._32) < eps && Math.abs(this._33-that._33) < eps;
+};
+
+/**
+ * Decomposes the matrix into a translation, rotation, scale,
+ * and scale orientation. Any projection information is discarded.
+ * The decomposition depends upon choice of center point for rotation and scaling,
+ * which is optional as the last parameter.
+ * @param {x3dom.fields.SFVec3f} translation - 3D vector to be filled with the translation values
+ * @param {x3dom.fields.Quaternion} rotation - quaternion to be filled with the rotation values
+ * @param {x3dom.fields.SFVec3f} scaleFactor - 3D vector to be filled with the scale factors
+ * @param {x3dom.fields.Quaternion} scaleOrientation - rotation (quaternion) to be applied before scaling
+ * @param {x3dom.fields.SFVec3f} [center=undefined] - center point for rotation and scaling, if not origin
+ */
+x3dom.fields.SFMatrix4f.prototype.getTransform = function(
+ translation, rotation, scaleFactor, scaleOrientation, center)
+{
+ var m = null;
+
+ if (arguments.length > 4) {
+ m = x3dom.fields.SFMatrix4f.translation(center.negate());
+ m = m.mult(this);
+
+ var c = x3dom.fields.SFMatrix4f.translation(center);
+ m = m.mult(c);
+ }
+ else {
+ m = x3dom.fields.SFMatrix4f.copy(this);
+ }
+
+ var flip = m.decompose(translation, rotation, scaleFactor, scaleOrientation);
+
+ scaleFactor.setValues(scaleFactor.multiply(flip));
+};
+
+/**
+ * Computes the decomposition of the given 4x4 affine matrix M as M = T F R SO S SO^t,
+ * where T is a translation matrix, F is +/- I (a reflection), R is a rotation matrix,
+ * SO is a rotation matrix and S is a (nonuniform) scale matrix.
+ * @param {x3dom.fields.SFVec3f} t - 3D vector to be filled with the translation values
+ * @param {x3dom.fields.Quaternion} r - quaternion to be filled with the rotation values
+ * @param {x3dom.fields.SFVec3f} s - 3D vector to be filled with the scale factors
+ * @param {x3dom.fields.Quaternion} so - rotation (quaternion) to be applied before scaling
+ * @returns {Number} signum of determinant of the transposed adjoint upper 3x3 matrix
+ */
+x3dom.fields.SFMatrix4f.prototype.decompose = function(t, r, s, so)
+{
+ var A = x3dom.fields.SFMatrix4f.copy(this);
+
+ var Q = x3dom.fields.SFMatrix4f.identity(),
+ S = x3dom.fields.SFMatrix4f.identity(),
+ SO = x3dom.fields.SFMatrix4f.identity();
+
+ t.x = A._03;
+ t.y = A._13;
+ t.z = A._23;
+
+ A._03 = 0.0;
+ A._13 = 0.0;
+ A._23 = 0.0;
+
+ A._30 = 0.0;
+ A._31 = 0.0;
+ A._32 = 0.0;
+
+ var det = A.polarDecompose(Q, S);
+ var f = 1.0;
+
+ if (det < 0.0) {
+ Q = Q.negate();
+ f = -1.0;
+ }
+
+ r.setValue(Q);
+
+ S.spectralDecompose(SO, s);
+
+ so.setValue(SO);
+
+ return f;
+};
+
+/**
+ * Performs a polar decomposition of this matrix A into two matrices Q and S, so that A = QS
+ * @param {x3dom.fields.SFMatrix4f} Q - first resulting matrix
+ * @param {x3dom.fields.SFMatrix4f} S - first resulting matrix
+ * @returns {Number} determinant of the transposed adjoint upper 3x3 matrix
+ */
+x3dom.fields.SFMatrix4f.prototype.polarDecompose = function(Q, S)
+{
+ var TOL = 0.000000000001;
+
+ var Mk = this.transpose();
+ var Ek = x3dom.fields.SFMatrix4f.identity();
+
+ var Mk_one = Mk.norm1_3x3();
+ var Mk_inf = Mk.normInf_3x3();
+
+ var MkAdjT;
+ var MkAdjT_one, MkAdjT_inf;
+ var Ek_one, Mk_det;
+
+ do
+ {
+ // compute transpose of adjoint
+ MkAdjT = Mk.adjointT_3x3();
+
+ // Mk_det = det(Mk) -- computed from the adjoint
+ Mk_det = Mk._00 * MkAdjT._00 +
+ Mk._01 * MkAdjT._01 +
+ Mk._02 * MkAdjT._02;
+
+ //TODO: should this be a close to zero test ?
+ if (Mk_det == 0.0)
+ {
+ x3dom.debug.logWarning("polarDecompose: Mk_det == 0.0");
+ break;
+ }
+
+ MkAdjT_one = MkAdjT.norm1_3x3();
+ MkAdjT_inf = MkAdjT.normInf_3x3();
+
+ // compute update factors
+ var gamma = Math.sqrt( Math.sqrt((MkAdjT_one * MkAdjT_inf) /
+ (Mk_one * Mk_inf)) / Math.abs(Mk_det) );
+
+ var g1 = 0.5 * gamma;
+ var g2 = 0.5 / (gamma * Mk_det);
+
+ Ek.setValues(Mk);
+
+ Mk = Mk.multiply (g1); // this does:
+ Mk = Mk.addScaled(MkAdjT, g2); // Mk = g1 * Mk + g2 * MkAdjT
+ Ek = Ek.addScaled(Mk, -1.0); // Ek -= Mk;
+
+ Ek_one = Ek.norm1_3x3();
+ Mk_one = Mk.norm1_3x3();
+ Mk_inf = Mk.normInf_3x3();
+
+ } while (Ek_one > (Mk_one * TOL));
+
+ Q.setValues(Mk.transpose());
+ S.setValues(Mk.mult(this));
+
+ for (var i = 0; i < 3; ++i)
+ {
+ for (var j = i; j < 3; ++j)
+ {
+ S['_'+j+i] = 0.5 * (S['_'+j+i] + S['_'+i+j]);
+ S['_'+i+j] = 0.5 * (S['_'+j+i] + S['_'+i+j]);
+ }
+ }
+
+ return Mk_det;
+};
+
+/**
+ * Performs a spectral decomposition of this matrix.
+ * @param {x3dom.fields.SFMatrix4f} SO - resulting matrix
+ * @param {x3dom.fields.SFVec3f} k - resulting vector
+ */
+x3dom.fields.SFMatrix4f.prototype.spectralDecompose = function(SO, k)
+{
+ var next = [1, 2, 0];
+ var maxIterations = 20;
+ var diag = [this._00, this._11, this._22];
+ var offDiag = [this._12, this._20, this._01];
+
+ for (var iter = 0; iter < maxIterations; ++iter)
+ {
+ var sm = Math.abs(offDiag[0]) + Math.abs(offDiag[1]) + Math.abs(offDiag[2]);
+
+ if (sm == 0) {
+ break;
+ }
+
+ for (var i = 2; i >= 0; --i)
+ {
+ var p = next[i];
+ var q = next[p];
+
+ var absOffDiag = Math.abs(offDiag[i]);
+ var g = 100.0 * absOffDiag;
+
+ if (absOffDiag > 0.0)
+ {
+ var t = 0, h = diag[q] - diag[p];
+ var absh = Math.abs(h);
+
+ if (absh + g == absh)
+ {
+ t = offDiag[i] / h;
+ }
+ else
+ {
+ var theta = 0.5 * h / offDiag[i];
+ t = 1.0 / (Math.abs(theta) + Math.sqrt(theta * theta + 1.0));
+
+ t = theta < 0.0 ? -t : t;
+ }
+
+ var c = 1.0 / Math.sqrt(t * t + 1.0);
+ var s = t * c;
+
+ var tau = s / (c + 1.0);
+ var ta = t * offDiag[i];
+
+ offDiag[i] = 0.0;
+
+ diag[p] -= ta;
+ diag[q] += ta;
+
+ var offDiagq = offDiag[q];
+
+ offDiag[q] -= s * (offDiag[p] + tau * offDiagq);
+ offDiag[p] += s * (offDiagq - tau * offDiag[p]);
+
+ for (var j = 2; j >= 0; --j)
+ {
+ var a = SO['_'+j+p];
+ var b = SO['_'+j+q];
+
+ SO['_'+j+p] -= s * (b + tau * a);
+ SO['_'+j+q] += s * (a - tau * b);
+ }
+ }
+ }
+ }
+
+ k.x = diag[0];
+ k.y = diag[1];
+ k.z = diag[2];
+};
+
+/**
+ * Computes the logarithm of this matrix, assuming that its determinant is greater than zero.
+ * @returns {x3dom.fields.SFMatrix4f} log of matrix
+ */
+x3dom.fields.SFMatrix4f.prototype.log = function () {
+ var maxiter = 12;
+ var eps = 1e-12;
+
+ var A = x3dom.fields.SFMatrix4f.copy(this),
+ Z = x3dom.fields.SFMatrix4f.copy(this);
+
+ // Take repeated square roots to reduce spectral radius
+ Z._00 -= 1;
+ Z._11 -= 1;
+ Z._22 -= 1;
+ Z._33 -= 1;
+
+ var k = 0;
+
+ while (Z.normInfinity() > 0.5)
+ {
+ A = A.sqrt();
+ Z.setValues(A);
+
+ Z._00 -= 1;
+ Z._11 -= 1;
+ Z._22 -= 1;
+ Z._33 -= 1;
+
+ k++;
+ }
+
+ A._00 -= 1;
+ A._11 -= 1;
+ A._22 -= 1;
+ A._33 -= 1;
+
+ A = A.negate();
+ Z.setValues(A);
+
+ var result = x3dom.fields.SFMatrix4f.copy(A);
+ var i = 1;
+
+ while (Z.normInfinity() > eps && i < maxiter)
+ {
+ Z = Z.mult(A);
+ i++;
+
+ result = result.addScaled(Z, 1.0 / i);
+ }
+
+ return result.multiply( -(1 << k) );
+};
+
+/**
+ * Computes the exponential of this matrix.
+ * @returns {x3dom.fields.SFMatrix4f} exp of matrix
+ */
+x3dom.fields.SFMatrix4f.prototype.exp = function () {
+ var q = 6;
+ var A = x3dom.fields.SFMatrix4f.copy(this),
+ D = x3dom.fields.SFMatrix4f.identity(),
+ N = x3dom.fields.SFMatrix4f.identity(),
+ result = x3dom.fields.SFMatrix4f.identity();
+ var k = 0, c = 1.0;
+
+ var j = 1.0 + parseInt(Math.log(A.normInfinity() / 0.693));
+ //var j = 1.0 + (Math.log(A.normInfinity() / 0.693) | 0);
+
+ if (j < 0) {
+ j = 0;
+ }
+
+ A = A.multiply(1.0 / (1 << j));
+
+ for (k = 1; k <= q; k++)
+ {
+ c *= (q - k + 1) / (k * (2 * q - k + 1));
+
+ result = A.mult(result);
+
+ N = N.addScaled(result, c);
+
+ if (k % 2) {
+ D = D.addScaled(result, -c);
+ }
+ else {
+ D = D.addScaled(result, c);
+ }
+ }
+
+ result = D.inverse().mult(N);
+
+ for (k = 0; k < j; k++)
+ {
+ result = result.mult(result);
+ }
+
+ return result;
+};
+
+/**
+ * Computes a determinant for a 3x3 matrix m, given as values in row major order.
+ * @param {Number} a1 - value of m at (0,0)
+ * @param {Number} a2 - value of m at (0,1)
+ * @param {Number} a3 - value of m at (0,2)
+ * @param {Number} b1 - value of m at (1,0)
+ * @param {Number} b2 - value of m at (1,1)
+ * @param {Number} b3 - value of m at (1,2)
+ * @param {Number} c1 - value of m at (2,0)
+ * @param {Number} c2 - value of m at (2,1)
+ * @param {Number} c3 - value of m at (2,2)
+ * @returns {Number} determinant
+ */
+x3dom.fields.SFMatrix4f.prototype.det3 = function (a1, a2, a3, b1, b2, b3, c1, c2, c3) {
+ return ((a1 * b2 * c3) + (a2 * b3 * c1) + (a3 * b1 * c2) -
+ (a1 * b3 * c2) - (a2 * b1 * c3) - (a3 * b2 * c1));
+};
+
+/**
+ * Computes the determinant of this matrix.
+ * @returns {Number} determinant
+ */
+x3dom.fields.SFMatrix4f.prototype.det = function () {
+ var a1 = this._00;
+ var b1 = this._10;
+ var c1 = this._20;
+ var d1 = this._30;
+
+ var a2 = this._01;
+ var b2 = this._11;
+ var c2 = this._21;
+ var d2 = this._31;
+
+ var a3 = this._02;
+ var b3 = this._12;
+ var c3 = this._22;
+ var d3 = this._32;
+
+ var a4 = this._03;
+ var b4 = this._13;
+ var c4 = this._23;
+ var d4 = this._33;
+
+ return (a1 * this.det3(b2, b3, b4, c2, c3, c4, d2, d3, d4) -
+ b1 * this.det3(a2, a3, a4, c2, c3, c4, d2, d3, d4) +
+ c1 * this.det3(a2, a3, a4, b2, b3, b4, d2, d3, d4) -
+ d1 * this.det3(a2, a3, a4, b2, b3, b4, c2, c3, c4));
+};
+
+/**
+ * Computes the inverse of this matrix, given that it is not singular.
+ * @returns {x3dom.fields.SFMatrix4f}
+ */
+x3dom.fields.SFMatrix4f.prototype.inverse = function () {
+ var a1 = this._00;
+ var b1 = this._10;
+ var c1 = this._20;
+ var d1 = this._30;
+
+ var a2 = this._01;
+ var b2 = this._11;
+ var c2 = this._21;
+ var d2 = this._31;
+
+ var a3 = this._02;
+ var b3 = this._12;
+ var c3 = this._22;
+ var d3 = this._32;
+
+ var a4 = this._03;
+ var b4 = this._13;
+ var c4 = this._23;
+ var d4 = this._33;
+
+ var rDet = this.det();
+
+ //if (Math.abs(rDet) < 1e-30)
+ if (rDet == 0)
+ {
+ x3dom.debug.logWarning("Invert matrix: singular matrix, no inverse!");
+ return x3dom.fields.SFMatrix4f.identity();
+ }
+
+ rDet = 1.0 / rDet;
+
+ return new x3dom.fields.SFMatrix4f(
+ +this.det3(b2, b3, b4, c2, c3, c4, d2, d3, d4) * rDet,
+ -this.det3(a2, a3, a4, c2, c3, c4, d2, d3, d4) * rDet,
+ +this.det3(a2, a3, a4, b2, b3, b4, d2, d3, d4) * rDet,
+ -this.det3(a2, a3, a4, b2, b3, b4, c2, c3, c4) * rDet,
+ -this.det3(b1, b3, b4, c1, c3, c4, d1, d3, d4) * rDet,
+ +this.det3(a1, a3, a4, c1, c3, c4, d1, d3, d4) * rDet,
+ -this.det3(a1, a3, a4, b1, b3, b4, d1, d3, d4) * rDet,
+ +this.det3(a1, a3, a4, b1, b3, b4, c1, c3, c4) * rDet,
+ +this.det3(b1, b2, b4, c1, c2, c4, d1, d2, d4) * rDet,
+ -this.det3(a1, a2, a4, c1, c2, c4, d1, d2, d4) * rDet,
+ +this.det3(a1, a2, a4, b1, b2, b4, d1, d2, d4) * rDet,
+ -this.det3(a1, a2, a4, b1, b2, b4, c1, c2, c4) * rDet,
+ -this.det3(b1, b2, b3, c1, c2, c3, d1, d2, d3) * rDet,
+ +this.det3(a1, a2, a3, c1, c2, c3, d1, d2, d3) * rDet,
+ -this.det3(a1, a2, a3, b1, b2, b3, d1, d2, d3) * rDet,
+ +this.det3(a1, a2, a3, b1, b2, b3, c1, c2, c3) * rDet
+ );
+};
+
+/**
+ * Returns an array of 2*3 = 6 euler angles (in radians), assuming that this is a rotation matrix.
+ * The first three and the second three values are alternatives for the three euler angles,
+ * where each of the two cases leads to the same resulting rotation.
+ * @returns {Number[]}
+ */
+x3dom.fields.SFMatrix4f.prototype.getEulerAngles = function() {
+ var theta_1, theta_2, theta;
+ var phi_1, phi_2, phi;
+ var psi_1, psi_2, psi;
+ var cos_theta_1, cos_theta_2;
+
+ if ( Math.abs((Math.abs(this._20) - 1.0)) > 0.0001) {
+ theta_1 = -Math.asin(this._20);
+ theta_2 = Math.PI - theta_1;
+
+ cos_theta_1 = Math.cos(theta_1);
+ cos_theta_2 = Math.cos(theta_2);
+
+ psi_1 = Math.atan2(this._21 / cos_theta_1, this._22 / cos_theta_1);
+ psi_2 = Math.atan2(this._21 / cos_theta_2, this._22 / cos_theta_2);
+
+ phi_1 = Math.atan2(this._10 / cos_theta_1, this._00 / cos_theta_1);
+ phi_2 = Math.atan2(this._10 / cos_theta_2, this._00 / cos_theta_2);
+
+ return [psi_1, theta_1, phi_1,
+ psi_2, theta_2, phi_2];
+ }
+ else {
+ phi = 0;
+
+ if (this._20 == -1.0) {
+ theta = Math.PI / 2.0;
+ psi = phi + Math.atan2(this._01, this._02);
+ }
+ else {
+ theta = -(Math.PI / 2.0);
+ psi = -phi + Math.atan2(-this._01, -this._02);
+ }
+
+ return [psi, theta, phi,
+ psi, theta, phi];
+ }
+};
+
+/**
+ * Converts this matrix to a string representation, where all entries are separated by commas,
+ * and where rows are additionally separated by linebreaks.
+ * @returns {String}
+ */
+x3dom.fields.SFMatrix4f.prototype.toString = function () {
+ return '\n' +
+ this._00.toFixed(6)+', '+this._01.toFixed(6)+', '+
+ this._02.toFixed(6)+', '+this._03.toFixed(6)+', \n'+
+ this._10.toFixed(6)+', '+this._11.toFixed(6)+', '+
+ this._12.toFixed(6)+', '+this._13.toFixed(6)+', \n'+
+ this._20.toFixed(6)+', '+this._21.toFixed(6)+', '+
+ this._22.toFixed(6)+', '+this._23.toFixed(6)+', \n'+
+ this._30.toFixed(6)+', '+this._31.toFixed(6)+', '+
+ this._32.toFixed(6)+', '+this._33.toFixed(6);
+};
+
+/**
+ * Fills the values of this matrix from a string, where the entries are separated
+ * by commas and given in column-major order.
+ * @param {String} str - the string representation
+ */
+x3dom.fields.SFMatrix4f.prototype.setValueByStr = function(str) {
+ var needTranspose = false;
+ var val = /matrix.*\((.+)\)/;
+ // check if matrix is set via CSS string
+ if (val.exec(str)) {
+ str = RegExp.$1;
+ needTranspose = true;
+ }
+ var arr = Array.map(str.split(/[,\s]+/), function (n) { return +n; });
+ if (arr.length >= 16)
+ {
+ if (!needTranspose) {
+ this._00 = arr[0]; this._01 = arr[1]; this._02 = arr[2]; this._03 = arr[3];
+ this._10 = arr[4]; this._11 = arr[5]; this._12 = arr[6]; this._13 = arr[7];
+ this._20 = arr[8]; this._21 = arr[9]; this._22 = arr[10]; this._23 = arr[11];
+ this._30 = arr[12]; this._31 = arr[13]; this._32 = arr[14]; this._33 = arr[15];
+ }
+ else {
+ this._00 = arr[0]; this._01 = arr[4]; this._02 = arr[8]; this._03 = arr[12];
+ this._10 = arr[1]; this._11 = arr[5]; this._12 = arr[9]; this._13 = arr[13];
+ this._20 = arr[2]; this._21 = arr[6]; this._22 = arr[10]; this._23 = arr[14];
+ this._30 = arr[3]; this._31 = arr[7]; this._32 = arr[11]; this._33 = arr[15];
+ }
+ }
+ else if (arr.length === 6) {
+ this._00 = arr[0]; this._01 = arr[1]; this._02 = 0; this._03 = arr[4];
+ this._10 = arr[2]; this._11 = arr[3]; this._12 = 0; this._13 = arr[5];
+ this._20 = 0; this._21 = 0; this._22 = 1; this._23 = 0;
+ this._30 = 0; this._31 = 0; this._32 = 0; this._33 = 1;
+ }
+ else {
+ x3dom.debug.logWarning("SFMatrix4f - can't parse string: " + str);
+ }
+ return this;
+};
+
+
+///////////////////////////////////////////////////////////////////////////////
+/** SFVec2f constructor.
+ @class Represents a SFVec2f
+ */
+x3dom.fields.SFVec2f = function(x, y) {
+ if (arguments.length === 0) {
+ this.x = 0;
+ this.y = 0;
+ }
+ else {
+ this.x = x;
+ this.y = y;
+ }
+};
+
+
+x3dom.fields.SFVec2f.copy = function(v) {
+ return new x3dom.fields.SFVec2f(v.x, v.y);
+};
+
+x3dom.fields.SFVec2f.parse = function (str) {
+ var m = /^\s*([+\-]?\d*\.*\d*[eE]?[+\-]?\d*?)\s*,?\s*([+\-]?\d*\.*\d*[eE]?[+\-]?\d*?)\s*$/.exec(str);
+ return new x3dom.fields.SFVec2f(+m[1], +m[2]);
+};
+
+x3dom.fields.SFVec2f.prototype.copy = function() {
+ return x3dom.fields.SFVec2f.copy(this);
+};
+
+x3dom.fields.SFVec2f.prototype.setValues = function (that) {
+ this.x = that.x;
+ this.y = that.y;
+};
+
+x3dom.fields.SFVec2f.prototype.at = function (i) {
+ switch(i) {
+ case 0: return this.x;
+ case 1: return this.y;
+ default: return this.x;
+ }
+};
+
+x3dom.fields.SFVec2f.prototype.add = function (that) {
+ return new x3dom.fields.SFVec2f(this.x+that.x, this.y+that.y);
+};
+
+x3dom.fields.SFVec2f.prototype.subtract = function (that) {
+ return new x3dom.fields.SFVec2f(this.x-that.x, this.y-that.y);
+};
+
+x3dom.fields.SFVec2f.prototype.negate = function () {
+ return new x3dom.fields.SFVec2f(-this.x, -this.y);
+};
+
+x3dom.fields.SFVec2f.prototype.dot = function (that) {
+ return this.x * that.x + this.y * that.y;
+};
+
+x3dom.fields.SFVec2f.prototype.reflect = function (n) {
+ var d2 = this.dot(n)*2;
+ return new x3dom.fields.SFVec2f(this.x-d2*n.x, this.y-d2*n.y);
+};
+
+x3dom.fields.SFVec2f.prototype.normalize = function() {
+ var n = this.length();
+ if (n) { n = 1.0 / n; }
+ return new x3dom.fields.SFVec2f(this.x*n, this.y*n);
+};
+
+x3dom.fields.SFVec2f.prototype.multComponents = function (that) {
+ return new x3dom.fields.SFVec2f(this.x*that.x, this.y*that.y);
+};
+
+x3dom.fields.SFVec2f.prototype.multiply = function (n) {
+ return new x3dom.fields.SFVec2f(this.x*n, this.y*n);
+};
+
+x3dom.fields.SFVec2f.prototype.divide = function (n) {
+ var denom = n ? (1.0 / n) : 1.0;
+ return new x3dom.fields.SFVec2f(this.x*denom, this.y*denom);
+};
+
+x3dom.fields.SFVec2f.prototype.equals = function (that, eps) {
+ return Math.abs(this.x - that.x) < eps &&
+ Math.abs(this.y - that.y) < eps;
+};
+
+x3dom.fields.SFVec2f.prototype.length = function() {
+ return Math.sqrt((this.x*this.x) + (this.y*this.y));
+};
+
+x3dom.fields.SFVec2f.prototype.toGL = function () {
+ return [ this.x, this.y ];
+};
+
+x3dom.fields.SFVec2f.prototype.toString = function () {
+ return this.x + " " + this.y;
+};
+
+x3dom.fields.SFVec2f.prototype.setValueByStr = function(str) {
+ var m = /^\s*([+\-]?\d*\.*\d*[eE]?[+\-]?\d*?)\s*,?\s*([+\-]?\d*\.*\d*[eE]?[+\-]?\d*?)\s*$/.exec(str);
+ this.x = +m[1];
+ this.y = +m[2];
+ return this;
+};
+
+
+///////////////////////////////////////////////////////////////////////////////
+/** SFVec3f constructor.
+ @class Represents a SFVec3f
+ */
+x3dom.fields.SFVec3f = function(x, y, z) {
+ if (arguments.length === 0) {
+ this.x = 0;
+ this.y = 0;
+ this.z = 0;
+ }
+ else {
+ this.x = x;
+ this.y = y;
+ this.z = z;
+ }
+};
+
+x3dom.fields.SFVec3f.NullVector = new x3dom.fields.SFVec3f(0, 0, 0);
+x3dom.fields.SFVec3f.OneVector = new x3dom.fields.SFVec3f(1, 1, 1);
+
+x3dom.fields.SFVec3f.copy = function(v) {
+ return new x3dom.fields.SFVec3f(v.x, v.y, v.z);
+};
+
+x3dom.fields.SFVec3f.MIN = function() {
+ return new x3dom.fields.SFVec3f(-Number.MAX_VALUE, -Number.MAX_VALUE, -Number.MAX_VALUE);
+};
+
+x3dom.fields.SFVec3f.MAX = function() {
+ return new x3dom.fields.SFVec3f(Number.MAX_VALUE, Number.MAX_VALUE, Number.MAX_VALUE);
+};
+
+x3dom.fields.SFVec3f.parse = function (str) {
+ try {
+ var m = /^\s*([+\-]?\d*\.*\d*[eE]?[+\-]?\d*?)\s*,?\s*([+\-]?\d*\.*\d*[eE]?[+\-]?\d*?)\s*,?\s*([+\-]?\d*\.*\d*[eE]?[+\-]?\d*?)\s*$/.exec(str);
+ return new x3dom.fields.SFVec3f(+m[1], +m[2], +m[3]);
+ }
+ catch (e) {
+ // allow automatic type conversion as is convenient for shaders
+ var c = x3dom.fields.SFColor.colorParse(str);
+ return new x3dom.fields.SFVec3f(c.r, c.g, c.b);
+ }
+};
+
+x3dom.fields.SFVec3f.prototype.copy = function() {
+ return x3dom.fields.SFVec3f.copy(this);
+};
+
+x3dom.fields.SFVec3f.prototype.setValues = function (that) {
+ this.x = that.x;
+ this.y = that.y;
+ this.z = that.z;
+};
+
+x3dom.fields.SFVec3f.prototype.at = function (i) {
+ switch(i) {
+ case 0: return this.x;
+ case 1: return this.y;
+ case 2: return this.z;
+ default: return this.x;
+ }
+};
+
+x3dom.fields.SFVec3f.prototype.add = function (that) {
+ return new x3dom.fields.SFVec3f(this.x + that.x, this.y + that.y, this.z + that.z);
+};
+
+x3dom.fields.SFVec3f.prototype.addScaled = function (that, s) {
+ return new x3dom.fields.SFVec3f(this.x + s*that.x, this.y + s*that.y, this.z + s*that.z);
+};
+
+x3dom.fields.SFVec3f.prototype.subtract = function (that) {
+ return new x3dom.fields.SFVec3f(this.x - that.x, this.y - that.y, this.z - that.z);
+};
+
+x3dom.fields.SFVec3f.prototype.negate = function () {
+ return new x3dom.fields.SFVec3f(-this.x, -this.y, -this.z);
+};
+
+x3dom.fields.SFVec3f.prototype.dot = function (that) {
+ return (this.x*that.x + this.y*that.y + this.z*that.z);
+};
+
+x3dom.fields.SFVec3f.prototype.cross = function (that) {
+ return new x3dom.fields.SFVec3f( this.y*that.z - this.z*that.y,
+ this.z*that.x - this.x*that.z,
+ this.x*that.y - this.y*that.x );
+};
+
+x3dom.fields.SFVec3f.prototype.reflect = function (n) {
+ var d2 = this.dot(n)*2;
+ return new x3dom.fields.SFVec3f(this.x - d2*n.x, this.y - d2*n.y, this.z - d2*n.z);
+};
+
+x3dom.fields.SFVec3f.prototype.length = function() {
+ return Math.sqrt((this.x*this.x) + (this.y*this.y) + (this.z*this.z));
+};
+
+x3dom.fields.SFVec3f.prototype.normalize = function() {
+ var n = this.length();
+ if (n) { n = 1.0 / n; }
+ return new x3dom.fields.SFVec3f(this.x*n, this.y*n, this.z*n);
+};
+
+x3dom.fields.SFVec3f.prototype.multComponents = function (that) {
+ return new x3dom.fields.SFVec3f(this.x*that.x, this.y*that.y, this.z*that.z);
+};
+
+x3dom.fields.SFVec3f.prototype.multiply = function (n) {
+ return new x3dom.fields.SFVec3f(this.x*n, this.y*n, this.z*n);
+};
+
+x3dom.fields.SFVec3f.prototype.divide = function (n) {
+ var denom = n ? (1.0 / n) : 1.0;
+ return new x3dom.fields.SFVec3f(this.x*denom, this.y*denom, this.z*denom);
+};
+
+x3dom.fields.SFVec3f.prototype.equals = function (that, eps) {
+ return Math.abs(this.x - that.x) < eps &&
+ Math.abs(this.y - that.y) < eps &&
+ Math.abs(this.z - that.z) < eps;
+};
+
+x3dom.fields.SFVec3f.prototype.toGL = function () {
+ return [ this.x, this.y, this.z ];
+};
+
+x3dom.fields.SFVec3f.prototype.toString = function () {
+ return this.x + " " + this.y + " " + this.z;
+};
+
+x3dom.fields.SFVec3f.prototype.setValueByStr = function(str) {
+ try {
+ var m = /^\s*([+\-]?\d*\.*\d*[eE]?[+\-]?\d*?)\s*,?\s*([+\-]?\d*\.*\d*[eE]?[+\-]?\d*?)\s*,?\s*([+\-]?\d*\.*\d*[eE]?[+\-]?\d*?)\s*$/.exec(str);
+ this.x = +m[1];
+ this.y = +m[2];
+ this.z = +m[3];
+ }
+ catch (e) {
+ // allow automatic type conversion as is convenient for shaders
+ var c = x3dom.fields.SFColor.colorParse(str);
+ this.x = c.r;
+ this.y = c.g;
+ this.z = c.b;
+ }
+ return this;
+};
+
+
+///////////////////////////////////////////////////////////////////////////////
+/** SFVec4f constructor.
+ @class Represents a SFVec4f
+ */
+x3dom.fields.SFVec4f = function(x, y, z, w) {
+ if (arguments.length === 0) {
+ this.x = 0;
+ this.y = 0;
+ this.z = 0;
+ this.w = 0;
+ }
+ else {
+ this.x = x;
+ this.y = y;
+ this.z = z;
+ this.w = w;
+ }
+};
+
+x3dom.fields.SFVec4f.copy = function(v) {
+ return new x3dom.fields.SFVec4f(v.x, v.y, v.z, v.w);
+};
+
+
+x3dom.fields.SFVec4f.prototype.copy = function() {
+ return x3dom.fields.SFVec4f(this);
+};
+
+x3dom.fields.SFVec4f.parse = function (str) {
+ var m = /^\s*([+\-]?\d*\.*\d*[eE]?[+\-]?\d*?)\s*,?\s*([+\-]?\d*\.*\d*[eE]?[+\-]?\d*?)\s*,?\s*([+\-]?\d*\.*\d*[eE]?[+\-]?\d*?)\s*,?\s*([+\-]?\d*\.*\d*[eE]?[+\-]?\d*?)\s*$/.exec(str);
+ return new x3dom.fields.SFVec4f(+m[1], +m[2], +m[3], +m[4]);
+};
+
+x3dom.fields.SFVec4f.prototype.setValueByStr = function(str) {
+ var m = /^\s*([+\-]?\d*\.*\d*[eE]?[+\-]?\d*?)\s*,?\s*([+\-]?\d*\.*\d*[eE]?[+\-]?\d*?)\s*,?\s*([+\-]?\d*\.*\d*[eE]?[+\-]?\d*?)\s*,?\s*([+\-]?\d*\.*\d*[eE]?[+\-]?\d*?)\s*$/.exec(str);
+ this.x = +m[1];
+ this.y = +m[2];
+ this.z = +m[3];
+ this.w = +m[4];
+ return this;
+};
+
+x3dom.fields.SFVec4f.prototype.toGL = function () {
+ return [ this.x, this.y, this.z, this.w ];
+};
+
+x3dom.fields.SFVec4f.prototype.toString = function () {
+ return this.x + " " + this.y + " " + this.z + " " + this.w;
+};
+
+
+///////////////////////////////////////////////////////////////////////////////
+/** Quaternion constructor.
+ @class Represents a Quaternion
+ */
+x3dom.fields.Quaternion = function(x, y, z, w) {
+ if (arguments.length === 0) {
+ this.x = 0;
+ this.y = 0;
+ this.z = 0;
+ this.w = 1;
+ }
+ else {
+ this.x = x;
+ this.y = y;
+ this.z = z;
+ this.w = w;
+ }
+};
+
+x3dom.fields.Quaternion.copy = function(v) {
+ return new x3dom.fields.Quaternion(v.x, v.y, v.z, v.w);
+};
+
+x3dom.fields.Quaternion.prototype.multiply = function (that) {
+ return new x3dom.fields.Quaternion(
+ this.w*that.x + this.x*that.w + this.y*that.z - this.z*that.y,
+ this.w*that.y + this.y*that.w + this.z*that.x - this.x*that.z,
+ this.w*that.z + this.z*that.w + this.x*that.y - this.y*that.x,
+ this.w*that.w - this.x*that.x - this.y*that.y - this.z*that.z
+ );
+};
+
+x3dom.fields.Quaternion.parseAxisAngle = function (str) {
+ var m = /^\s*([+\-]?\d*\.*\d*[eE]?[+\-]?\d*?)\s*,?\s*([+\-]?\d*\.*\d*[eE]?[+\-]?\d*?)\s*,?\s*([+\-]?\d*\.*\d*[eE]?[+\-]?\d*?)\s*,?\s*([+\-]?\d*\.*\d*[eE]?[+\-]?\d*?)\s*$/.exec(str);
+ return x3dom.fields.Quaternion.axisAngle(new x3dom.fields.SFVec3f(+m[1], +m[2], +m[3]), +m[4]);
+};
+
+x3dom.fields.Quaternion.axisAngle = function (axis, a) {
+ var t = axis.length();
+
+ if (t > x3dom.fields.Eps)
+ {
+ var s = Math.sin(a/2) / t;
+ var c = Math.cos(a/2);
+ return new x3dom.fields.Quaternion(axis.x*s, axis.y*s, axis.z*s, c);
+ }
+ else
+ {
+ return new x3dom.fields.Quaternion(0, 0, 0, 1);
+ }
+};
+
+x3dom.fields.Quaternion.prototype.copy = function() {
+ return x3dom.fields.Quaternion(this);
+};
+
+x3dom.fields.Quaternion.prototype.toMatrix = function () {
+ var xx = this.x * this.x;
+ var xy = this.x * this.y;
+ var xz = this.x * this.z;
+ var yy = this.y * this.y;
+ var yz = this.y * this.z;
+ var zz = this.z * this.z;
+ var wx = this.w * this.x;
+ var wy = this.w * this.y;
+ var wz = this.w * this.z;
+
+ return new x3dom.fields.SFMatrix4f(
+ 1 - 2 * (yy + zz), 2 * (xy - wz), 2 * (xz + wy), 0,
+ 2 * (xy + wz), 1 - 2 * (xx + zz), 2 * (yz - wx), 0,
+ 2 * (xz - wy), 2 * (yz + wx), 1 - 2 * (xx + yy), 0,
+ 0, 0, 0, 1
+ );
+};
+
+x3dom.fields.Quaternion.prototype.toAxisAngle = function()
+{
+ var x = 0, y = 0, z = 0;
+ var s = 0, a = 0;
+ var that = this;
+
+ if ( this.w > 1 )
+ {
+ that = x3dom.fields.Quaternion.normalize( this );
+ }
+
+ a = 2 * Math.acos( that.w );
+ s = Math.sqrt( 1 - that.w * that.w );
+
+ if ( s == 0 ) //< x3dom.fields.Eps )
+ {
+ x = that.x;
+ y = that.y;
+ z = that.z;
+ }
+ else
+ {
+ x = that.x / s;
+ y = that.y / s;
+ z = that.z / s;
+ }
+
+ return [ new x3dom.fields.SFVec3f(x,y,z), a ];
+};
+
+x3dom.fields.Quaternion.prototype.angle = function()
+{
+ return 2 * Math.acos(this.w);
+};
+
+x3dom.fields.Quaternion.prototype.setValue = function(matrix)
+{
+ var tr, s = 1;
+ var qt = [0, 0, 0];
+
+ var i = 0, j = 0, k = 0;
+ var nxt = [1, 2, 0];
+
+ tr = matrix._00 + matrix._11 + matrix._22;
+
+ if (tr > 0.0)
+ {
+ s = Math.sqrt(tr + 1.0);
+
+ this.w = s * 0.5;
+
+ s = 0.5 / s;
+
+ this.x = (matrix._21 - matrix._12) * s;
+ this.y = (matrix._02 - matrix._20) * s;
+ this.z = (matrix._10 - matrix._01) * s;
+ }
+ else
+ {
+ if (matrix._11 > matrix._00) {
+ i = 1;
+ }
+ else {
+ i = 0;
+ }
+
+ if (matrix._22 > matrix.at(i, i)) {
+ i = 2;
+ }
+
+ j = nxt[i];
+ k = nxt[j];
+
+ s = Math.sqrt(matrix.at(i, i) - (matrix.at(j, j) + matrix.at(k, k)) + 1.0);
+
+ qt[i] = s * 0.5;
+ s = 0.5 / s;
+
+ this.w = (matrix.at(k, j) - matrix.at(j, k)) * s;
+
+ qt[j] = (matrix.at(j, i) + matrix.at(i, j)) * s;
+ qt[k] = (matrix.at(k, i) + matrix.at(i, k)) * s;
+
+ this.x = qt[0];
+ this.y = qt[1];
+ this.z = qt[2];
+ }
+
+ if (this.w > 1.0 || this.w < -1.0)
+ {
+ var errThreshold = 1 + (x3dom.fields.Eps * 100);
+
+ if (this.w > errThreshold || this.w < -errThreshold)
+ {
+ // When copying, then everything, incl. the famous OpenSG MatToQuat bug
+ x3dom.debug.logInfo("MatToQuat: BUG: |quat[4]| (" + this.w +") >> 1.0 !");
+ }
+
+ if (this.w > 1.0) {
+ this.w = 1.0;
+ }
+ else {
+ this.w = -1.0;
+ }
+ }
+};
+
+x3dom.fields.Quaternion.prototype.setFromEuler = function (alpha, beta, gamma) {
+ var sx = Math.sin(alpha * 0.5);
+ var cx = Math.cos(alpha * 0.5);
+ var sy = Math.sin(beta * 0.5);
+ var cy = Math.cos(beta * 0.5);
+ var sz = Math.sin(gamma * 0.5);
+ var cz = Math.cos(gamma * 0.5);
+
+ this.x = (sx * cy * cz) - (cx * sy * sz);
+ this.y = (cx * sy * cz) + (sx * cy * sz);
+ this.z = (cx * cy * sz) - (sx * sy * cz);
+ this.w = (cx * cy * cz) + (sx * sy * sz);
+};
+
+x3dom.fields.Quaternion.prototype.dot = function (that) {
+ return this.x*that.x + this.y*that.y + this.z*that.z + this.w*that.w;
+};
+
+x3dom.fields.Quaternion.prototype.add = function (that) {
+ return new x3dom.fields.Quaternion(this.x + that.x, this.y + that.y, this.z + that.z, this.w + that.w);
+};
+
+x3dom.fields.Quaternion.prototype.subtract = function (that) {
+ return new x3dom.fields.Quaternion(this.x - that.x, this.y - that.y, this.z - that.z, this.w - that.w);
+};
+
+x3dom.fields.Quaternion.prototype.setValues = function (that) {
+ this.x = that.x;
+ this.y = that.y;
+ this.z = that.z;
+ this.w = that.w;
+};
+
+x3dom.fields.Quaternion.prototype.equals = function (that, eps) {
+ return (this.dot(that) >= 1.0 - eps);
+};
+
+x3dom.fields.Quaternion.prototype.multScalar = function (s) {
+ return new x3dom.fields.Quaternion(this.x*s, this.y*s, this.z*s, this.w*s);
+};
+
+x3dom.fields.Quaternion.prototype.normalize = function (that) {
+ var d2 = this.dot(that);
+ var id = 1.0;
+ if (d2) { id = 1.0 / Math.sqrt(d2); }
+ return new x3dom.fields.Quaternion(this.x*id, this.y*id, this.z*id, this.w*id);
+};
+
+x3dom.fields.Quaternion.prototype.negate = function() {
+ return new x3dom.fields.Quaternion(-this.x, -this.y, -this.z, -this.w);
+};
+
+x3dom.fields.Quaternion.prototype.inverse = function () {
+ return new x3dom.fields.Quaternion(-this.x, -this.y, -this.z, this.w);
+};
+
+x3dom.fields.Quaternion.prototype.slerp = function (that, t) {
+ // calculate the cosine
+ var cosom = this.dot(that);
+ var rot1;
+
+ // adjust signs if necessary
+ if (cosom < 0.0)
+ {
+ cosom = -cosom;
+ rot1 = that.negate();
+ }
+ else
+ {
+ rot1 = new x3dom.fields.Quaternion(that.x, that.y, that.z, that.w);
+ }
+
+ // calculate interpolating coeffs
+ var scalerot0, scalerot1;
+
+ if ((1.0 - cosom) > 0.00001)
+ {
+ // standard case
+ var omega = Math.acos(cosom);
+ var sinom = Math.sin(omega);
+ scalerot0 = Math.sin((1.0 - t) * omega) / sinom;
+ scalerot1 = Math.sin(t * omega) / sinom;
+ }
+ else
+ {
+ // rot0 and rot1 very close - just do linear interp.
+ scalerot0 = 1.0 - t;
+ scalerot1 = t;
+ }
+
+ // build the new quaternion
+ return this.multScalar(scalerot0).add(rot1.multScalar(scalerot1));
+};
+
+x3dom.fields.Quaternion.rotateFromTo = function (fromVec, toVec) {
+ var from = fromVec.normalize();
+ var to = toVec.normalize();
+ var cost = from.dot(to);
+
+ // check for degeneracies
+ if (cost > 0.99999)
+ {
+ // vectors are parallel
+ return new x3dom.fields.Quaternion(0, 0, 0, 1);
+ }
+ else if (cost < -0.99999)
+ {
+ // vectors are opposite
+ // find an axis to rotate around, which should be
+ // perpendicular to the original axis
+ // Try cross product with (1,0,0) first, if that's one of our
+ // original vectors then try (0,1,0).
+ var cAxis = new x3dom.fields.SFVec3f(1, 0, 0);
+
+ var tmp = from.cross(cAxis);
+
+ if (tmp.length() < 0.00001)
+ {
+ cAxis.x = 0;
+ cAxis.y = 1;
+ cAxis.z = 0;
+
+ tmp = from.cross(cAxis);
+ }
+ tmp = tmp.normalize();
+
+ return x3dom.fields.Quaternion.axisAngle(tmp, Math.PI);
+ }
+
+ var axis = fromVec.cross(toVec);
+ axis = axis.normalize();
+
+ // use half-angle formulae
+ // sin^2 t = ( 1 - cos (2t) ) / 2
+ var s = Math.sqrt(0.5 * (1.0 - cost));
+ axis = axis.multiply(s);
+
+ // scale the axis by the sine of half the rotation angle to get
+ // the normalized quaternion
+ // cos^2 t = ( 1 + cos (2t) ) / 2
+ // w part is cosine of half the rotation angle
+ s = Math.sqrt(0.5 * (1.0 + cost));
+
+ return new x3dom.fields.Quaternion(axis.x, axis.y, axis.z, s);
+};
+
+x3dom.fields.Quaternion.prototype.toGL = function () {
+ var val = this.toAxisAngle();
+ return [ val[0].x, val[0].y, val[0].z, val[1] ];
+};
+
+x3dom.fields.Quaternion.prototype.toString = function () {
+ return this.x + " " + this.y + " " + this.z + ", " + this.w;
+};
+
+x3dom.fields.Quaternion.prototype.setValueByStr = function(str) {
+ var m = /^\s*([+\-]?\d*\.*\d*[eE]?[+\-]?\d*?)\s*,?\s*([+\-]?\d*\.*\d*[eE]?[+\-]?\d*?)\s*,?\s*([+\-]?\d*\.*\d*[eE]?[+\-]?\d*?)\s*,?\s*([+\-]?\d*\.*\d*[eE]?[+\-]?\d*?)\s*$/.exec(str);
+ var quat = x3dom.fields.Quaternion.axisAngle(new x3dom.fields.SFVec3f(+m[1], +m[2], +m[3]), +m[4]);
+ this.x = quat.x;
+ this.y = quat.y;
+ this.z = quat.z;
+ this.w = quat.w;
+ return this;
+};
+
+
+///////////////////////////////////////////////////////////////////////////////
+/** SFColor constructor.
+ @class Represents a SFColor
+ */
+x3dom.fields.SFColor = function(r, g, b) {
+ if (arguments.length === 0) {
+ this.r = 0;
+ this.g = 0;
+ this.b = 0;
+ }
+ else {
+ this.r = r;
+ this.g = g;
+ this.b = b;
+ }
+};
+
+x3dom.fields.SFColor.parse = function(str) {
+ try {
+ var m = /^\s*([+\-]?\d*\.*\d*[eE]?[+\-]?\d*?)\s*,?\s*([+\-]?\d*\.*\d*[eE]?[+\-]?\d*?)\s*,?\s*([+\-]?\d*\.*\d*[eE]?[+\-]?\d*?)\s*$/.exec(str);
+ return new x3dom.fields.SFColor( +m[1], +m[2], +m[3] );
+ }
+ catch (e) {
+ return x3dom.fields.SFColor.colorParse(str);
+ }
+};
+
+x3dom.fields.SFColor.copy = function(that) {
+ return new x3dom.fields.SFColor(that.r, that.g, that.b);
+};
+
+x3dom.fields.SFColor.prototype.copy = function() {
+ return x3dom.fields.SFColor.copy(this);
+};
+
+x3dom.fields.SFColor.prototype.setHSV = function (h, s, v) {
+ x3dom.debug.logWarning("SFColor.setHSV() NYI");
+};
+
+x3dom.fields.SFColor.prototype.getHSV = function () {
+ var h = 0, s = 0, v = 0;
+ x3dom.debug.logWarning("SFColor.getHSV() NYI");
+ return [ h, s, v ];
+};
+
+x3dom.fields.SFColor.prototype.setValues = function (color) {
+ this.r = color.r;
+ this.g = color.g;
+ this.b = color.b;
+};
+
+x3dom.fields.SFColor.prototype.equals = function (that, eps) {
+ return Math.abs(this.r - that.r) < eps &&
+ Math.abs(this.g - that.g) < eps &&
+ Math.abs(this.b - that.b) < eps;
+};
+
+x3dom.fields.SFColor.prototype.add = function (that) {
+ return new x3dom.fields.SFColor(this.r + that.r, this.g + that.g, this.b + that.b);
+};
+
+x3dom.fields.SFColor.prototype.subtract = function (that) {
+ return new x3dom.fields.SFColor(this.r - that.r, this.g - that.g, this.b - that.b);
+};
+
+x3dom.fields.SFColor.prototype.multiply = function (n) {
+ return new x3dom.fields.SFColor(this.r*n, this.g*n, this.b*n);
+};
+
+x3dom.fields.SFColor.prototype.toGL = function () {
+ return [ this.r, this.g, this.b ];
+};
+
+x3dom.fields.SFColor.prototype.toString = function() {
+ return this.r + " " + this.g + " " + this.b;
+};
+
+x3dom.fields.SFColor.prototype.setValueByStr = function(str) {
+ try {
+ var m = /^\s*([+\-]?\d*\.*\d*[eE]?[+\-]?\d*?)\s*,?\s*([+\-]?\d*\.*\d*[eE]?[+\-]?\d*?)\s*,?\s*([+\-]?\d*\.*\d*[eE]?[+\-]?\d*?)\s*$/.exec(str);
+ this.r = +m[1];
+ this.g = +m[2];
+ this.b = +m[3];
+ }
+ catch (e) {
+ var c = x3dom.fields.SFColor.colorParse(str);
+ this.r = c.r;
+ this.g = c.g;
+ this.b = c.b;
+ }
+ return this;
+};
+
+x3dom.fields.SFColor.colorParse = function(color) {
+ var red = 0, green = 0, blue = 0;
+
+ // definition of css color names
+ var color_names = {
+ aliceblue: 'f0f8ff', antiquewhite: 'faebd7', aqua: '00ffff',
+ aquamarine: '7fffd4', azure: 'f0ffff', beige: 'f5f5dc',
+ bisque: 'ffe4c4', black: '000000', blanchedalmond: 'ffebcd',
+ blue: '0000ff', blueviolet: '8a2be2', brown: 'a52a2a',
+ burlywood: 'deb887', cadetblue: '5f9ea0', chartreuse: '7fff00',
+ chocolate: 'd2691e', coral: 'ff7f50', cornflowerblue: '6495ed',
+ cornsilk: 'fff8dc', crimson: 'dc143c', cyan: '00ffff',
+ darkblue: '00008b', darkcyan: '008b8b', darkgoldenrod: 'b8860b',
+ darkgray: 'a9a9a9', darkgreen: '006400', darkkhaki: 'bdb76b',
+ darkmagenta: '8b008b', darkolivegreen: '556b2f',darkorange: 'ff8c00',
+ darkorchid: '9932cc', darkred: '8b0000', darksalmon: 'e9967a',
+ darkseagreen: '8fbc8f', darkslateblue: '483d8b',darkslategray: '2f4f4f',
+ darkturquoise: '00ced1',darkviolet: '9400d3', deeppink: 'ff1493',
+ deepskyblue: '00bfff', dimgray: '696969', dodgerblue: '1e90ff',
+ feldspar: 'd19275', firebrick: 'b22222', floralwhite: 'fffaf0',
+ forestgreen: '228b22', fuchsia: 'ff00ff', gainsboro: 'dcdcdc',
+ ghostwhite: 'f8f8ff', gold: 'ffd700', goldenrod: 'daa520',
+ gray: '808080', green: '008000', greenyellow: 'adff2f',
+ honeydew: 'f0fff0', hotpink: 'ff69b4', indianred : 'cd5c5c',
+ indigo : '4b0082', ivory: 'fffff0', khaki: 'f0e68c',
+ lavender: 'e6e6fa', lavenderblush: 'fff0f5',lawngreen: '7cfc00',
+ lemonchiffon: 'fffacd', lightblue: 'add8e6', lightcoral: 'f08080',
+ lightcyan: 'e0ffff', lightgoldenrodyellow: 'fafad2', lightgrey: 'd3d3d3',
+ lightgreen: '90ee90', lightpink: 'ffb6c1', lightsalmon: 'ffa07a',
+ lightseagreen: '20b2aa',lightskyblue: '87cefa', lightslateblue: '8470ff',
+ lightslategray: '778899',lightsteelblue: 'b0c4de',lightyellow: 'ffffe0',
+ lime: '00ff00', limegreen: '32cd32', linen: 'faf0e6',
+ magenta: 'ff00ff', maroon: '800000', mediumaquamarine: '66cdaa',
+ mediumblue: '0000cd', mediumorchid: 'ba55d3', mediumpurple: '9370d8',
+ mediumseagreen: '3cb371',mediumslateblue: '7b68ee', mediumspringgreen: '00fa9a',
+ mediumturquoise: '48d1cc',mediumvioletred: 'c71585',midnightblue: '191970',
+ mintcream: 'f5fffa', mistyrose: 'ffe4e1', moccasin: 'ffe4b5',
+ navajowhite: 'ffdead', navy: '000080', oldlace: 'fdf5e6',
+ olive: '808000', olivedrab: '6b8e23', orange: 'ffa500',
+ orangered: 'ff4500', orchid: 'da70d6', palegoldenrod: 'eee8aa',
+ palegreen: '98fb98', paleturquoise: 'afeeee',palevioletred: 'd87093',
+ papayawhip: 'ffefd5', peachpuff: 'ffdab9', peru: 'cd853f',
+ pink: 'ffc0cb', plum: 'dda0dd', powderblue: 'b0e0e6',
+ purple: '800080', red: 'ff0000', rosybrown: 'bc8f8f',
+ royalblue: '4169e1', saddlebrown: '8b4513', salmon: 'fa8072',
+ sandybrown: 'f4a460', seagreen: '2e8b57', seashell: 'fff5ee',
+ sienna: 'a0522d', silver: 'c0c0c0', skyblue: '87ceeb',
+ slateblue: '6a5acd', slategray: '708090', snow: 'fffafa',
+ springgreen: '00ff7f', steelblue: '4682b4', tan: 'd2b48c',
+ teal: '008080', thistle: 'd8bfd8', tomato: 'ff6347',
+ turquoise: '40e0d0', violet: 'ee82ee', violetred: 'd02090',
+ wheat: 'f5deb3', white: 'ffffff', whitesmoke: 'f5f5f5',
+ yellow: 'ffff00', yellowgreen: '9acd32'
+ };
+
+ if (color_names[color]) {
+ // first check if color is given as colorname
+ color = "#" + color_names[color];
+ }
+
+ if (color.substr && color.substr(0,1) === "#") {
+ color = color.substr(1);
+ var len = color.length;
+
+ if (len === 6) {
+ red = parseInt("0x"+color.substr(0,2), 16) / 255.0;
+ green = parseInt("0x"+color.substr(2,2), 16) / 255.0;
+ blue = parseInt("0x"+color.substr(4,2), 16) / 255.0;
+ }
+ else if (len === 3) {
+ red = parseInt("0x"+color.substr(0,1), 16) / 15.0;
+ green = parseInt("0x"+color.substr(1,1), 16) / 15.0;
+ blue = parseInt("0x"+color.substr(2,1), 16) / 15.0;
+ }
+ }
+
+ return new x3dom.fields.SFColor( red, green, blue );
+};
+
+
+///////////////////////////////////////////////////////////////////////////////
+/** SFColorRGBA constructor.
+ @class Represents a SFColorRGBA
+ */
+x3dom.fields.SFColorRGBA = function(r, g, b, a) {
+ if (arguments.length === 0) {
+ this.r = 0;
+ this.g = 0;
+ this.b = 0;
+ this.a = 1;
+ }
+ else {
+ this.r = r;
+ this.g = g;
+ this.b = b;
+ this.a = a;
+ }
+};
+
+x3dom.fields.SFColorRGBA.parse = function(str) {
+ try {
+ var m = /^([+\-]?\d*\.*\d*[eE]?[+\-]?\d*?)\s*,?\s*([+\-]?\d*\.*\d*[eE]?[+\-]?\d*?)\s*,?\s*([+\-]?\d*\.*\d*[eE]?[+\-]?\d*?)\s*,?\s*([+\-]?\d*\.*\d*[eE]?[+\-]?\d*?)$/.exec(str);
+ return new x3dom.fields.SFColorRGBA( +m[1], +m[2], +m[3], +m[4] );
+ }
+ catch (e) {
+ return x3dom.fields.SFColorRGBA.colorParse(str);
+ }
+};
+
+x3dom.fields.SFColorRGBA.copy = function(that) {
+ return new x3dom.fields.SFColorRGBA(that.r, that.g, that.b, that.a);
+};
+
+x3dom.fields.SFColorRGBA.prototype.copy = function() {
+ return x3dom.fields.SFColorRGBA.copy(this);
+};
+
+x3dom.fields.SFColorRGBA.prototype.setValues = function (color) {
+ this.r = color.r;
+ this.g = color.g;
+ this.b = color.b;
+ this.a = color.a;
+};
+
+x3dom.fields.SFColorRGBA.prototype.equals = function (that, eps) {
+ return Math.abs(this.r - that.r) < eps &&
+ Math.abs(this.g - that.g) < eps &&
+ Math.abs(this.b - that.b) < eps &&
+ Math.abs(this.a - that.a) < eps;
+};
+
+x3dom.fields.SFColorRGBA.prototype.toGL = function () {
+ return [ this.r, this.g, this.b, this.a ];
+};
+
+x3dom.fields.SFColorRGBA.prototype.toString = function() {
+ return this.r + " " + this.g + " " + this.b + " " + this.a;
+};
+
+x3dom.fields.SFColorRGBA.prototype.setValueByStr = function(str) {
+ try {
+ var m = /^([+\-]?\d*\.*\d*[eE]?[+\-]?\d*?)\s*,?\s*([+\-]?\d*\.*\d*[eE]?[+\-]?\d*?)\s*,?\s*([+\-]?\d*\.*\d*[eE]?[+\-]?\d*?)\s*,?\s*([+\-]?\d*\.*\d*[eE]?[+\-]?\d*?)$/.exec(str);
+ this.r = +m[1];
+ this.g = +m[2];
+ this.b = +m[3];
+ this.a = +m[4];
+ }
+ catch (e) {
+ var c = x3dom.fields.SFColorRGBA.colorParse(str);
+ this.r = c.r;
+ this.g = c.g;
+ this.b = c.b;
+ this.a = c.a;
+ }
+ return this;
+};
+
+x3dom.fields.SFColorRGBA.prototype.toUint = function() {
+ return ((Math.round(this.r * 255) << 24) |
+ (Math.round(this.g * 255) << 16) |
+ (Math.round(this.b * 255) << 8) |
+ Math.round(this.a * 255)) >>> 0;
+};
+
+x3dom.fields.SFColorRGBA.colorParse = function(color) {
+ var red = 0, green = 0, blue = 0, alpha = 0;
+
+ // definition of css color names
+ var color_names = {
+ aliceblue: 'f0f8ff', antiquewhite: 'faebd7', aqua: '00ffff',
+ aquamarine: '7fffd4', azure: 'f0ffff', beige: 'f5f5dc',
+ bisque: 'ffe4c4', black: '000000', blanchedalmond: 'ffebcd',
+ blue: '0000ff', blueviolet: '8a2be2', brown: 'a52a2a',
+ burlywood: 'deb887', cadetblue: '5f9ea0', chartreuse: '7fff00',
+ chocolate: 'd2691e', coral: 'ff7f50', cornflowerblue: '6495ed',
+ cornsilk: 'fff8dc', crimson: 'dc143c', cyan: '00ffff',
+ darkblue: '00008b', darkcyan: '008b8b', darkgoldenrod: 'b8860b',
+ darkgray: 'a9a9a9', darkgreen: '006400', darkkhaki: 'bdb76b',
+ darkmagenta: '8b008b', darkolivegreen: '556b2f',darkorange: 'ff8c00',
+ darkorchid: '9932cc', darkred: '8b0000', darksalmon: 'e9967a',
+ darkseagreen: '8fbc8f', darkslateblue: '483d8b',darkslategray: '2f4f4f',
+ darkturquoise: '00ced1',darkviolet: '9400d3', deeppink: 'ff1493',
+ deepskyblue: '00bfff', dimgray: '696969', dodgerblue: '1e90ff',
+ feldspar: 'd19275', firebrick: 'b22222', floralwhite: 'fffaf0',
+ forestgreen: '228b22', fuchsia: 'ff00ff', gainsboro: 'dcdcdc',
+ ghostwhite: 'f8f8ff', gold: 'ffd700', goldenrod: 'daa520',
+ gray: '808080', green: '008000', greenyellow: 'adff2f',
+ honeydew: 'f0fff0', hotpink: 'ff69b4', indianred : 'cd5c5c',
+ indigo : '4b0082', ivory: 'fffff0', khaki: 'f0e68c',
+ lavender: 'e6e6fa', lavenderblush: 'fff0f5',lawngreen: '7cfc00',
+ lemonchiffon: 'fffacd', lightblue: 'add8e6', lightcoral: 'f08080',
+ lightcyan: 'e0ffff', lightgoldenrodyellow: 'fafad2', lightgrey: 'd3d3d3',
+ lightgreen: '90ee90', lightpink: 'ffb6c1', lightsalmon: 'ffa07a',
+ lightseagreen: '20b2aa',lightskyblue: '87cefa', lightslateblue: '8470ff',
+ lightslategray: '778899',lightsteelblue: 'b0c4de',lightyellow: 'ffffe0',
+ lime: '00ff00', limegreen: '32cd32', linen: 'faf0e6',
+ magenta: 'ff00ff', maroon: '800000', mediumaquamarine: '66cdaa',
+ mediumblue: '0000cd', mediumorchid: 'ba55d3', mediumpurple: '9370d8',
+ mediumseagreen: '3cb371',mediumslateblue: '7b68ee', mediumspringgreen: '00fa9a',
+ mediumturquoise: '48d1cc',mediumvioletred: 'c71585',midnightblue: '191970',
+ mintcream: 'f5fffa', mistyrose: 'ffe4e1', moccasin: 'ffe4b5',
+ navajowhite: 'ffdead', navy: '000080', oldlace: 'fdf5e6',
+ olive: '808000', olivedrab: '6b8e23', orange: 'ffa500',
+ orangered: 'ff4500', orchid: 'da70d6', palegoldenrod: 'eee8aa',
+ palegreen: '98fb98', paleturquoise: 'afeeee',palevioletred: 'd87093',
+ papayawhip: 'ffefd5', peachpuff: 'ffdab9', peru: 'cd853f',
+ pink: 'ffc0cb', plum: 'dda0dd', powderblue: 'b0e0e6',
+ purple: '800080', red: 'ff0000', rosybrown: 'bc8f8f',
+ royalblue: '4169e1', saddlebrown: '8b4513', salmon: 'fa8072',
+ sandybrown: 'f4a460', seagreen: '2e8b57', seashell: 'fff5ee',
+ sienna: 'a0522d', silver: 'c0c0c0', skyblue: '87ceeb',
+ slateblue: '6a5acd', slategray: '708090', snow: 'fffafa',
+ springgreen: '00ff7f', steelblue: '4682b4', tan: 'd2b48c',
+ teal: '008080', thistle: 'd8bfd8', tomato: 'ff6347',
+ turquoise: '40e0d0', violet: 'ee82ee', violetred: 'd02090',
+ wheat: 'f5deb3', white: 'ffffff', whitesmoke: 'f5f5f5',
+ yellow: 'ffff00', yellowgreen: '9acd32'
+ };
+
+ if (color_names[color]) {
+ // first check if color is given as colorname
+ color = "#" + color_names[color] + "ff";
+ }
+
+ if (color.substr && color.substr(0,1) === "#") {
+ color = color.substr(1);
+ var len = color.length;
+
+ if (len === 8) {
+ red = parseInt("0x"+color.substr(0,2), 16) / 255.0;
+ green = parseInt("0x"+color.substr(2,2), 16) / 255.0;
+ blue = parseInt("0x"+color.substr(4,2), 16) / 255.0;
+ alpha = parseInt("0x"+color.substr(6,2), 16) / 255.0;
+ }
+ else if (len === 6) {
+ red = parseInt("0x"+color.substr(0,2), 16) / 255.0;
+ green = parseInt("0x"+color.substr(2,2), 16) / 255.0;
+ blue = parseInt("0x"+color.substr(4,2), 16) / 255.0;
+ alpha = 1.0;
+ }
+ else if (len === 4) {
+ red = parseInt("0x"+color.substr(0,1), 16) / 15.0;
+ green = parseInt("0x"+color.substr(1,1), 16) / 15.0;
+ blue = parseInt("0x"+color.substr(2,1), 16) / 15.0;
+ alpha = parseInt("0x"+color.substr(3,1), 16) / 15.0;
+ }
+ else if (len === 3) {
+ red = parseInt("0x"+color.substr(0,1), 16) / 15.0;
+ green = parseInt("0x"+color.substr(1,1), 16) / 15.0;
+ blue = parseInt("0x"+color.substr(2,1), 16) / 15.0;
+ alpha = 1.0;
+ }
+ }
+
+ return new x3dom.fields.SFColorRGBA( red, green, blue, alpha );
+};
+
+
+///////////////////////////////////////////////////////////////////////////////
+/** SFImage constructor.
+ @class Represents an SFImage
+ */
+x3dom.fields.SFImage = function(w, h, c, arr) {
+ if (arguments.length === 0 || !(arr && arr.map)) {
+ this.width = 0;
+ this.height = 0;
+ this.comp = 0;
+ this.array = [];
+ }
+ else {
+ this.width = w;
+ this.height = h;
+ this.comp = c;
+ var that = this.array;
+ arr.map( function(v) { that.push(v); }, this.array );
+ }
+};
+
+x3dom.fields.SFImage.parse = function(str) {
+ var img = new x3dom.fields.SFImage();
+ img.setValueByStr(str);
+ return img;
+};
+
+x3dom.fields.SFImage.copy = function(that) {
+ var destination = new x3dom.fields.SFImage();
+ destination.width = that.width;
+ destination.height = that.height;
+ destination.comp = that.comp;
+ destination.setPixels(that.array);
+ return destination;
+};
+
+x3dom.fields.SFImage.prototype.copy = function() {
+ return x3dom.fields.SFImage.copy(this);
+};
+
+x3dom.fields.SFImage.prototype.setValueByStr = function(str) {
+ var mc = str.match(/(\w+)/g);
+ var n = mc.length;
+ var c2 = 0;
+ var hex = "0123456789ABCDEF";
+
+ this.array = [];
+
+ if (n > 2) {
+ this.width = +mc[0];
+ this.height = +mc[1];
+ this.comp = +mc[2];
+ c2 = 2 * this.comp;
+ } else {
+ this.width = 0;
+ this.height = 0;
+ this.comp = 0;
+ return;
+ }
+
+ var len, i;
+ for (i=3; i<n; i++) {
+ var r, g, b, a;
+
+ if (!mc[i].substr) {
+ continue;
+ }
+
+ if (mc[i].substr(1,1).toLowerCase() !== "x") {
+ // Maybe optimize by directly parsing value!
+ var inp = parseInt(mc[i], 10);
+
+ if (this.comp === 1) {
+ r = inp;
+ this.array.push( r );
+ }
+ else if (this.comp === 2) {
+ r = inp >> 8 & 255;
+ g = inp & 255;
+ this.array.push( r, g );
+ }
+ else if (this.comp === 3) {
+ r = inp >> 16 & 255;
+ g = inp >> 8 & 255;
+ b = inp & 255;
+ this.array.push( r, g, b );
+ }
+ else if (this.comp === 4) {
+ r = inp >> 24 & 255;
+ g = inp >> 16 & 255;
+ b = inp >> 8 & 255;
+ a = inp & 255;
+ this.array.push( r, g, b, a );
+ }
+ }
+ else if (mc[i].substr(1,1).toLowerCase() === "x") {
+ mc[i] = mc[i].substr(2);
+ len = mc[i].length;
+
+ if (len === c2) {
+ if (this.comp === 1) {
+ r = parseInt("0x"+mc[i].substr(0,2), 16);
+ this.array.push( r );
+ }
+ else if (this.comp === 2) {
+ r = parseInt("0x"+mc[i].substr(0,2), 16);
+ g = parseInt("0x"+mc[i].substr(2,2), 16);
+ this.array.push( r, g );
+ }
+ else if (this.comp === 3) {
+ r = parseInt("0x"+mc[i].substr(0,2), 16);
+ g = parseInt("0x"+mc[i].substr(2,2), 16);
+ b = parseInt("0x"+mc[i].substr(4,2), 16);
+ this.array.push( r, g, b );
+ }
+ else if (this.comp === 4) {
+ r = parseInt("0x"+mc[i].substr(0,2), 16);
+ g = parseInt("0x"+mc[i].substr(2,2), 16);
+ b = parseInt("0x"+mc[i].substr(4,2), 16);
+ a = parseInt("0x"+mc[i].substr(6,2), 16);
+ this.array.push( r, g, b, a );
+ }
+ }
+ }
+ }
+};
+
+x3dom.fields.SFImage.prototype.setPixel = function(x, y, color) {
+ var startIdx = (y * this.width + x) * this.comp;
+
+ if (this.comp === 1 && startIdx < this.array.length) {
+ this.array[startIdx] = color.r * 255;
+ }
+ else if (this.comp === 2 && (startIdx+1) < this.array.length) {
+ this.array[startIdx ] = color.r * 255;
+ this.array[startIdx+1] = color.g * 255;
+ }
+ else if (this.comp === 3 && (startIdx+2) < this.array.length) {
+ this.array[startIdx ] = color.r * 255;
+ this.array[startIdx+1] = color.g * 255;
+ this.array[startIdx+2] = color.b * 255;
+ }
+ else if (this.comp === 4 && (startIdx+3) < this.array.length) {
+ this.array[startIdx ] = color.r * 255;
+ this.array[startIdx+1] = color.g * 255;
+ this.array[startIdx+2] = color.b * 255;
+ this.array[startIdx+3] = color.a * 255;
+ }
+};
+
+x3dom.fields.SFImage.prototype.getPixel = function(x, y) {
+ var startIdx = (y * this.width + x) * this.comp;
+
+ if (this.comp === 1 && startIdx < this.array.length) {
+ return new x3dom.fields.SFColorRGBA(this.array[startIdx] / 255,
+ 0,
+ 0,
+ 1);
+ }
+ else if (this.comp === 2 && (startIdx+1) < this.array.length) {
+ return new x3dom.fields.SFColorRGBA(this.array[startIdx] / 255,
+ this.array[startIdx+1] / 255,
+ 0,
+ 1);
+ }
+ else if (this.comp === 3 && (startIdx+2) < this.array.length) {
+ return new x3dom.fields.SFColorRGBA(this.array[startIdx] / 255,
+ this.array[startIdx+1] / 255,
+ this.array[startIdx+2] / 255,
+ 1);
+ }
+ else if (this.comp === 4 && (startIdx+3) < this.array.length) {
+ return new x3dom.fields.SFColorRGBA(this.array[startIdx] / 255,
+ this.array[startIdx+1] / 255,
+ this.array[startIdx+2] / 255,
+ this.array[startIdx+3] / 255);
+ }
+};
+
+x3dom.fields.SFImage.prototype.setPixels = function(pixels) {
+
+ var i, idx = 0;
+
+ if (this.comp === 1) {
+ for(i=0; i<pixels.length; i++) {
+ this.array[idx++] = pixels[i].r * 255;
+ }
+ }
+ else if (this.comp === 2) {
+ for(i=0; i<pixels.length; i++) {
+ this.array[idx++] = pixels[i].r * 255;
+ this.array[idx++] = pixels[i].g * 255;
+ }
+ }
+ else if (this.comp === 3) {
+ for(i=0; i<pixels.length; i++) {
+ this.array[idx++] = pixels[i].r * 255;
+ this.array[idx++] = pixels[i].g * 255;
+ this.array[idx++] = pixels[i].b * 255;
+ }
+ }
+ else if (this.comp === 4) {
+ for(i=0; i<pixels.length; i++) {
+ this.array[idx++] = pixels[i].r * 255;
+ this.array[idx++] = pixels[i].g * 255;
+ this.array[idx++] = pixels[i].b * 255;
+ this.array[idx++] = pixels[i].a * 255;
+ }
+ }
+};
+
+x3dom.fields.SFImage.prototype.getPixels = function() {
+ var i;
+ var pixels = [];
+
+ if (this.comp === 1) {
+ for (i=0; i<this.array.length; i+=this.comp){
+ pixels.push(new x3dom.fields.SFColorRGBA(this.array[i] / 255,
+ 0,
+ 0,
+ 1));
+ }
+ }
+ else if (this.comp === 2) {
+ for (i=0; i<this.array.length; i+=this.comp) {
+ pixels.push(new x3dom.fields.SFColorRGBA(this.array[i ] / 255,
+ this.array[i + 1] / 255,
+ 0,
+ 1));
+ }
+ }
+ else if (this.comp === 3) {
+ for (i=0; i<this.array.length; i+=this.comp) {
+ pixels.push(new x3dom.fields.SFColorRGBA(this.array[i ] / 255,
+ this.array[i + 1] / 255,
+ this.array[i + 2] / 255,
+ 1));
+ }
+ }
+ else if (this.comp === 4) {
+ for (i=0; i<this.array.length; i+=this.comp) {
+ pixels.push(new x3dom.fields.SFColorRGBA(this.array[i ] / 255,
+ this.array[i + 1] / 255,
+ this.array[i + 2] / 255,
+ this.array[i + 3] / 255));
+ }
+ }
+
+ return pixels;
+};
+
+x3dom.fields.SFImage.prototype.toGL = function() {
+ var a = [];
+
+ Array.map( this.array, function(c) {
+ a.push(c);
+ });
+
+ return a;
+};
+
+
+
+///////////////////////////////////////////////////////////////////////////////
+// Multi-Field Definitions
+///////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////
+/** MFColor constructor.
+ @class Represents a MFColor
+ */
+x3dom.fields.MFColor = function(colorArray) {
+
+ if (colorArray) {
+ var that = this;
+ colorArray.map( function(c) { that.push(c); }, this );
+ }
+};
+
+x3dom.fields.MFColor.copy = function(colorArray) {
+ var destination = new x3dom.fields.MFColor();
+ colorArray.map( function(v) { destination.push(v.copy()); }, this );
+ return destination;
+};
+
+x3dom.fields.MFColor.prototype = x3dom.extend([]);
+
+x3dom.fields.MFColor.parse = function(str) {
+ var mc = str.match(/([+\-0-9eE\.]+)/g);
+ var colors = [];
+ for (var i=0, n=mc?mc.length:0; i<n; i+=3) {
+ colors.push( new x3dom.fields.SFColor(+mc[i+0], +mc[i+1], +mc[i+2]) );
+ }
+
+ return new x3dom.fields.MFColor( colors );
+};
+
+x3dom.fields.MFColor.prototype.copy = function() {
+ return x3dom.fields.MFColor.copy(this);
+};
+
+x3dom.fields.MFColor.prototype.setValueByStr = function(str) {
+ this.length = 0;
+ var mc = str.match(/([+\-0-9eE\.]+)/g);
+ for (var i=0, n=mc?mc.length:0; i<n; i+=3) {
+ this.push( new x3dom.fields.SFColor(+mc[i+0], +mc[i+1], +mc[i+2]) );
+ }
+};
+
+x3dom.fields.MFColor.prototype.toGL = function() {
+ var a = [];
+
+ Array.map( this, function(c) {
+ a.push(c.r);
+ a.push(c.g);
+ a.push(c.b);
+ });
+
+ return a;
+};
+
+
+///////////////////////////////////////////////////////////////////////////////
+/** MFColorRGBA constructor.
+ @class Represents a MFColorRGBA
+ */
+x3dom.fields.MFColorRGBA = function(colorArray) {
+ if (colorArray) {
+ var that = this;
+ colorArray.map( function(c) { that.push(c); }, this );
+ }
+};
+
+x3dom.fields.MFColorRGBA.copy = function(colorArray) {
+ var destination = new x3dom.fields.MFColorRGBA();
+ colorArray.map( function(v) { destination.push(v.copy()); }, this );
+ return destination;
+};
+
+x3dom.fields.MFColorRGBA.prototype = x3dom.extend([]);
+
+x3dom.fields.MFColorRGBA.parse = function(str) {
+ var mc = str.match(/([+\-0-9eE\.]+)/g);
+ var colors = [];
+ for (var i=0, n=mc?mc.length:0; i<n; i+=4) {
+ colors.push( new x3dom.fields.SFColorRGBA(+mc[i+0], +mc[i+1], +mc[i+2], +mc[i+3]) );
+ }
+
+ return new x3dom.fields.MFColorRGBA( colors );
+};
+
+x3dom.fields.MFColorRGBA.prototype.copy = function() {
+ return x3dom.fields.MFColorRGBA.copy(this);
+};
+
+x3dom.fields.MFColorRGBA.prototype.setValueByStr = function(str) {
+ this.length = 0;
+ var mc = str.match(/([+\-0-9eE\.]+)/g);
+ for (var i=0, n=mc?mc.length:0; i<n; i+=4) {
+ this.push( new x3dom.fields.SFColorRGBA(+mc[i+0], +mc[i+1], +mc[i+2], +mc[i+3]) );
+ }
+};
+
+x3dom.fields.MFColorRGBA.prototype.toGL = function() {
+ var a = [];
+
+ Array.map( this, function(c) {
+ a.push(c.r);
+ a.push(c.g);
+ a.push(c.b);
+ a.push(c.a);
+ });
+
+ return a;
+};
+
+
+///////////////////////////////////////////////////////////////////////////////
+/** MFRotation constructor.
+ @class Represents a MFRotation
+ */
+x3dom.fields.MFRotation = function(rotArray) {
+ if (rotArray) {
+ var that = this;
+ rotArray.map( function(v) { that.push(v); }, this );
+ }
+};
+
+x3dom.fields.MFRotation.prototype = x3dom.extend([]);
+
+x3dom.fields.MFRotation.copy = function(rotationArray) {
+ var destination = new x3dom.fields.MFRotation();
+ rotationArray.map( function(v) { destination.push(v.copy()); }, this );
+ return destination;
+};
+
+x3dom.fields.MFRotation.prototype.copy = function() {
+ return x3dom.fields.MFRotation.copy(this);
+};
+
+x3dom.fields.MFRotation.parse = function(str) {
+ var mc = str.match(/([+\-0-9eE\.]+)/g);
+ var vecs = [];
+ for (var i=0, n=mc?mc.length:0; i<n; i+=4) {
+ vecs.push( x3dom.fields.Quaternion.axisAngle(new x3dom.fields.SFVec3f(+mc[i+0], +mc[i+1], +mc[i+2]), +mc[i+3]) );
+ }
+
+ // holds the quaternion representation as needed by interpolators etc.
+ return new x3dom.fields.MFRotation( vecs );
+};
+
+x3dom.fields.MFRotation.prototype.setValueByStr = function(str) {
+ this.length = 0;
+ var mc = str.match(/([+\-0-9eE\.]+)/g);
+ for (var i=0, n=mc?mc.length:0; i<n; i+=4) {
+ this.push( x3dom.fields.Quaternion.axisAngle(new x3dom.fields.SFVec3f(+mc[i+0], +mc[i+1], +mc[i+2]), +mc[i+3]) );
+ }
+};
+
+x3dom.fields.MFRotation.prototype.toGL = function() {
+ var a = [];
+
+ Array.map( this, function(c) {
+ var val = c.toAxisAngle();
+ a.push(val[0].x);
+ a.push(val[0].y);
+ a.push(val[0].z);
+ a.push(val[1]);
+ });
+
+ return a;
+};
+
+
+///////////////////////////////////////////////////////////////////////////////
+/** MFVec3f constructor.
+ @class Represents a MFVec3f
+ */
+x3dom.fields.MFVec3f = function(vec3Array) {
+ if (vec3Array) {
+ var that = this;
+ vec3Array.map( function(v) { that.push(v); }, this );
+ }
+};
+
+x3dom.fields.MFVec3f.prototype = x3dom.extend([]);
+
+x3dom.fields.MFVec3f.copy = function(vec3Array) {
+ var destination = new x3dom.fields.MFVec3f();
+ vec3Array.map( function(v) { destination.push(v.copy()); }, this );
+ return destination;
+};
+
+x3dom.fields.MFVec3f.parse = function(str) {
+ var mc = str.match(/([+\-0-9eE\.]+)/g);
+ var vecs = [];
+ for (var i=0, n=mc?mc.length:0; i<n; i+=3) {
+ vecs.push( new x3dom.fields.SFVec3f(+mc[i+0], +mc[i+1], +mc[i+2]) );
+ }
+
+ return new x3dom.fields.MFVec3f( vecs );
+};
+
+x3dom.fields.MFVec3f.prototype.copy = function()
+{
+ x3dom.fields.MFVec3f.copy(this);
+};
+
+x3dom.fields.MFVec3f.prototype.setValueByStr = function(str) {
+ this.length = 0;
+ var mc = str.match(/([+\-0-9eE\.]+)/g);
+ for (var i=0, n=mc?mc.length:0; i<n; i+=3) {
+ this.push( new x3dom.fields.SFVec3f(+mc[i+0], +mc[i+1], +mc[i+2]) );
+ }
+};
+
+x3dom.fields.MFVec3f.prototype.toGL = function() {
+ var a = [];
+
+ Array.map( this, function(c) {
+ a.push(c.x);
+ a.push(c.y);
+ a.push(c.z);
+ });
+
+ return a;
+};
+
+
+///////////////////////////////////////////////////////////////////////////////
+/** MFVec2f constructor.
+ @class Represents a MFVec2f
+ */
+x3dom.fields.MFVec2f = function(vec2Array) {
+ if (vec2Array) {
+ var that = this;
+ vec2Array.map( function(v) { that.push(v); }, this );
+ }
+};
+
+x3dom.fields.MFVec2f.prototype = x3dom.extend([]);
+
+x3dom.fields.MFVec2f.copy = function(vec2Array) {
+ var destination = new x3dom.fields.MFVec2f();
+ vec2Array.map( function(v) { destination.push(v.copy()); }, this );
+ return destination;
+};
+
+x3dom.fields.MFVec2f.parse = function(str) {
+ var mc = str.match(/([+\-0-9eE\.]+)/g);
+ var vecs = [];
+ for (var i=0, n=mc?mc.length:0; i<n; i+=2) {
+ vecs.push( new x3dom.fields.SFVec2f(+mc[i+0], +mc[i+1]) );
+ }
+
+ return new x3dom.fields.MFVec2f( vecs );
+};
+
+x3dom.fields.MFVec2f.prototype.copy = function() {
+ return x3dom.fields.MFVec2f.copy(this);
+};
+
+x3dom.fields.MFVec2f.prototype.setValueByStr = function(str) {
+ this.length = 0;
+ var mc = str.match(/([+\-0-9eE\.]+)/g);
+ for (var i=0, n=mc?mc.length:0; i<n; i+=2) {
+ this.push( new x3dom.fields.SFVec2f(+mc[i+0], +mc[i+1]) );
+ }
+};
+
+x3dom.fields.MFVec2f.prototype.toGL = function() {
+ var a = [];
+
+ Array.map( this, function(v) {
+ a.push(v.x);
+ a.push(v.y);
+ });
+
+ return a;
+};
+
+
+///////////////////////////////////////////////////////////////////////////////
+/** MFInt32 constructor.
+ @class Represents a MFInt32
+ */
+x3dom.fields.MFInt32 = function(array) {
+ if (array) {
+ var that = this;
+ array.map( function(v) { that.push(v); }, this );
+ }
+};
+
+x3dom.fields.MFInt32.prototype = x3dom.extend([]);
+
+x3dom.fields.MFInt32.copy = function(intArray) {
+ var destination = new x3dom.fields.MFInt32();
+ intArray.map( function(v) { destination.push(v); }, this );
+ return destination;
+};
+
+x3dom.fields.MFInt32.parse = function(str) {
+ var mc = str.match(/([+\-]?\d+\s*){1},?\s*/g);
+ var vals = [];
+ for (var i=0, n=mc?mc.length:0; i<n; ++i) {
+ vals.push( parseInt(mc[i], 10) );
+ }
+
+ return new x3dom.fields.MFInt32( vals );
+};
+
+x3dom.fields.MFInt32.prototype.copy = function() {
+ return x3dom.fields.MFInt32.copy(this);
+};
+
+x3dom.fields.MFInt32.prototype.setValueByStr = function(str) {
+ this.length = 0;
+ var mc = str.match(/([+\-]?\d+\s*){1},?\s*/g);
+ for (var i=0, n=mc?mc.length:0; i<n; ++i) {
+ this.push( parseInt(mc[i], 10) );
+ }
+};
+
+x3dom.fields.MFInt32.prototype.toGL = function() {
+ var a = [];
+
+ Array.map( this, function(v) {
+ a.push(v);
+ });
+
+ return a;
+};
+
+
+///////////////////////////////////////////////////////////////////////////////
+/** MFFloat constructor.
+ @class Represents a MFFloat
+ */
+x3dom.fields.MFFloat = function(array) {
+ if (array) {
+ var that = this;
+ array.map( function(v) { that.push(v); }, this );
+ }
+};
+
+x3dom.fields.MFFloat.prototype = x3dom.extend([]);
+
+x3dom.fields.MFFloat.copy = function(floatArray) {
+ var destination = new x3dom.fields.MFFloat();
+ floatArray.map( function(v) { destination.push(v); }, this );
+ return destination;
+};
+
+x3dom.fields.MFFloat.parse = function(str) {
+ var mc = str.match(/([+\-0-9eE\.]+)/g);
+ var vals = [];
+ for (var i=0, n=mc?mc.length:0; i<n; i++) {
+ vals.push( +mc[i] );
+ }
+
+ return new x3dom.fields.MFFloat( vals );
+};
+
+x3dom.fields.MFFloat.prototype.copy = function() {
+ return x3dom.fields.MFFloat.copy(this);
+};
+
+x3dom.fields.MFFloat.prototype.setValueByStr = function(str) {
+ this.length = 0;
+ var mc = str.match(/([+\-0-9eE\.]+)/g);
+ for (var i=0, n=mc?mc.length:0; i<n; i++) {
+ this.push( +mc[i] );
+ }
+};
+
+x3dom.fields.MFFloat.prototype.toGL = function() {
+ var a = [];
+
+ Array.map( this, function(v) {
+ a.push(v);
+ });
+
+ return a;
+};
+
+
+///////////////////////////////////////////////////////////////////////////////
+/** MFBoolean constructor.
+ @class Represents a MFBoolean
+ */
+x3dom.fields.MFBoolean = function(array) {
+ if (array) {
+ var that = this;
+ array.map( function(v) { that.push(v); }, this );
+ }
+};
+
+x3dom.fields.MFBoolean.prototype = x3dom.extend([]);
+
+x3dom.fields.MFBoolean.copy = function(boolArray) {
+ var destination = new x3dom.fields.MFBoolean();
+ boolArray.map( function(v) { destination.push(v); }, this );
+ return destination;
+};
+
+x3dom.fields.MFBoolean.parse = function(str) {
+ var mc = str.match(/(true|false|1|0)/ig);
+ var vals = [];
+ for (var i=0, n=mc?mc.length:0; i<n; i++) {
+ vals.push( (mc[i] == '1' || mc[i].toLowerCase() == 'true') );
+ }
+
+ return new x3dom.fields.MFBoolean( vals );
+};
+
+x3dom.fields.MFBoolean.prototype.copy = function() {
+ return x3dom.fields.MFBoolean.copy(this);
+};
+
+x3dom.fields.MFBoolean.prototype.setValueByStr = function(str) {
+ this.length = 0;
+ var mc = str.match(/(true|false|1|0)/ig);
+ for (var i=0, n=mc?mc.length:0; i<n; i++) {
+ this.push( (mc[i] == '1' || mc[i].toLowerCase() == 'true') );
+ }
+};
+
+x3dom.fields.MFBoolean.prototype.toGL = function() {
+ var a = [];
+
+ Array.map( this, function(v) {
+ a.push(v ? 1 : 0);
+ });
+
+ return a;
+};
+
+
+///////////////////////////////////////////////////////////////////////////////
+/** MFString constructor.
+ @class Represents a MFString
+ */
+x3dom.fields.MFString = function(strArray) {
+ if (strArray && strArray.map) {
+ var that = this;
+ strArray.map( function(v) { that.push(v); }, this );
+ }
+};
+
+x3dom.fields.MFString.prototype = x3dom.extend([]);
+
+x3dom.fields.MFString.copy = function(stringArray) {
+ var destination = new x3dom.fields.MFString();
+ stringArray.map( function(v) { destination.push(v); }, this );
+ return destination;
+};
+
+x3dom.fields.MFString.parse = function(str) {
+ var arr = [];
+ // ignore leading whitespace?
+ if (str.length && str[0] == '"') {
+ var m, re = /"((?:[^\\"]|\\\\|\\")*)"/g;
+ while ((m = re.exec(str))) {
+ var s = m[1].replace(/\\([\\"])/, "$1");
+ if (s !== undefined) {
+ arr.push(s);
+ }
+ }
+ }
+ else {
+ arr.push(str);
+ }
+ return new x3dom.fields.MFString( arr );
+};
+
+x3dom.fields.MFString.prototype.copy = function() {
+ return x3dom.fields.MFString.copy(this);
+};
+
+x3dom.fields.MFString.prototype.setValueByStr = function(str) {
+ this.length = 0;
+ // ignore leading whitespace?
+ if (str.length && str[0] == '"') {
+ var m, re = /"((?:[^\\"]|\\\\|\\")*)"/g;
+ while ((m = re.exec(str))) {
+ var s = m[1].replace(/\\([\\"])/, "$1");
+ if (s !== undefined) {
+ this.push(s);
+ }
+ }
+ }
+ else {
+ this.push(str);
+ }
+ return this;
+};
+
+x3dom.fields.MFString.prototype.toString = function () {
+ var str = "";
+ for (var i=0, n=this.length; i<n; i++) {
+ str = str + this[i] + " ";
+ }
+ return str;
+};
+
+
+
+///////////////////////////////////////////////////////////////////////////////
+// Single-/Multi-Field Node Definitions
+///////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////
+/** SFNode constructor.
+ @class Represents a SFNode
+ */
+x3dom.fields.SFNode = function(type) {
+ this.type = type;
+ this.node = null;
+};
+
+x3dom.fields.SFNode.prototype.hasLink = function(node) {
+ return (node ? (this.node === node) : this.node);
+};
+
+x3dom.fields.SFNode.prototype.addLink = function(node) {
+ this.node = node;
+ return true;
+};
+
+x3dom.fields.SFNode.prototype.rmLink = function(node) {
+ if (this.node === node) {
+ this.node = null;
+ return true;
+ }
+ else {
+ return false;
+ }
+};
+
+
+///////////////////////////////////////////////////////////////////////////////
+/** MFNode constructor.
+ @class Represents a MFNode
+ */
+x3dom.fields.MFNode = function(type) {
+ this.type = type;
+ this.nodes = [];
+};
+
+x3dom.fields.MFNode.prototype.hasLink = function(node) {
+ if (node) {
+ for (var i = 0, n = this.nodes.length; i < n; i++) {
+ if (this.nodes[i] === node) {
+ return true;
+ }
+ }
+ }
+ else {
+ return (this.length > 0);
+ }
+ return false;
+};
+
+x3dom.fields.MFNode.prototype.addLink = function(node) {
+ this.nodes.push (node);
+ return true;
+};
+
+x3dom.fields.MFNode.prototype.rmLink = function(node) {
+ for (var i = 0, n = this.nodes.length; i < n; i++) {
+ if (this.nodes[i] === node) {
+ this.nodes.splice(i,1);
+ return true;
+ }
+ }
+ return false;
+};
+
+x3dom.fields.MFNode.prototype.length = function() {
+ return this.nodes.length;
+};
+
+
+
+///////////////////////////////////////////////////////////////////////////////
+// Math Helper Class Definitions
+///////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////
+
+/**
+ * Line constructor.
+ * @param {SFVec3f} pos - anchor point of the line
+ * @param {SFVec3f} dir - direction of the line, must be normalized
+ * @class Represents a Line (as internal helper).
+ * A line has an origin and a vector that describes a direction, it is infinite in both directions.
+ */
+x3dom.fields.Line = function(pos, dir)
+{
+ if (arguments.length === 0)
+ {
+ this.pos = new x3dom.fields.SFVec3f(0, 0, 0);
+ this.dir = new x3dom.fields.SFVec3f(0, 0, 1);
+ }
+
+ this.pos = x3dom.fields.SFVec3f.copy(pos);
+ this.dir = x3dom.fields.SFVec3f.copy(dir);
+};
+
+/**
+ * For a given point, this function returns the closest point on this line.
+ * @param p {x3dom.fields.SFVec3f} - the point
+ * @returns {x3dom.fields.SFVec3f} the closest point
+ */
+x3dom.fields.Line.prototype.closestPoint = function(p)
+{
+ var distVec = p.subtract(this.pos);
+
+ //project the distance vector on the line
+ var projDist = distVec.dot(this.dir);
+
+ return this.pos.add(this.dir.multiply(projDist));
+};
+
+/**
+ * For a given point, this function returns the distance to the closest point on this line.
+ * @param p {x3dom.fields.SFVec3f} - the point
+ * @returns {Number} the distance to the closest point
+ */
+x3dom.fields.Line.prototype.shortestDistance = function(p)
+{
+ var distVec = p.subtract(this.pos);
+
+ //project the distance vector on the line
+ var projDist = distVec.dot(this.dir);
+
+ //subtract the projected distance vector, to obtain the part that is orthogonal to this line
+ return distVec.subtract(this.dir.multiply(projDist)).length();
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+/**
+ * Ray constructor.
+ * @param {SFVec3f} pos - anchor point of the ray
+ * @param {SFVec3f} dir - direction of the ray, must be normalized
+ * @class Represents a Ray (as internal helper).
+ * A ray is a special line that extends to only one direction from its origin.
+ */
+x3dom.fields.Ray = function(pos, dir)
+{
+ if (arguments.length === 0)
+ {
+ this.pos = new x3dom.fields.SFVec3f(0, 0, 0);
+ this.dir = new x3dom.fields.SFVec3f(0, 0, 1);
+ }
+ else
+ {
+ this.pos = new x3dom.fields.SFVec3f(pos.x, pos.y, pos.z);
+
+ var n = dir.length();
+ if (n) { n = 1.0 / n; }
+
+ this.dir = new x3dom.fields.SFVec3f(dir.x*n, dir.y*n, dir.z*n);
+ }
+
+ this.enter = 0;
+ this.exit = 0;
+ this.hitObject = null;
+ this.hitPoint = {};
+ this.dist = Number.MAX_VALUE;
+};
+
+x3dom.fields.Ray.prototype.toString = function () {
+ return 'Ray: [' + this.pos.toString() + '; ' + this.dir.toString() + ']';
+};
+
+/**
+ * Intersects this ray with a plane, defined by the given anchor point and normal.
+ * The result returned is the point of intersection, if any. If no point of intersection exists, null is returned.
+ * Null is also returned in case there is an infinite number of solutions (, i.e., if the ray origin lies in the plane).
+ *
+ * @param p {x3dom.fields.SFVec3f} - anchor point
+ * @param n {x3dom.fields.SFVec3f} - plane normal
+ * @returns {x3dom.fields.SFVec3f} the point of intersection, can be null
+ */
+x3dom.fields.Ray.prototype.intersectPlane = function(p, n)
+{
+ var result = null;
+
+ var alpha; //ray parameter, should be computed
+
+ var nDotDir = n.dot(this.dir);
+
+ //if the ray hits the plane, the plane normal and ray direction must be facing each other
+ if (nDotDir < 0.0)
+ {
+ alpha = (p.dot(n) - this.pos.dot(n)) / nDotDir;
+
+ result = this.pos.addScaled(this.dir, alpha);
+ }
+
+ return result;
+};
+
+/** intersect line with box volume given by low and high */
+x3dom.fields.Ray.prototype.intersect = function(low, high)
+{
+ var isect = 0.0;
+ var out = Number.MAX_VALUE;
+ var r, te, tl;
+
+ if (this.dir.x > x3dom.fields.Eps)
+ {
+ r = 1.0 / this.dir.x;
+
+ te = (low.x - this.pos.x) * r;
+ tl = (high.x - this.pos.x) * r;
+
+ if (tl < out){
+ out = tl;
+ }
+
+ if (te > isect){
+ isect = te;
+ }
+ }
+ else if (this.dir.x < -x3dom.fields.Eps)
+ {
+ r = 1.0 / this.dir.x;
+
+ te = (high.x - this.pos.x) * r;
+ tl = (low.x - this.pos.x) * r;
+
+ if (tl < out){
+ out = tl;
+ }
+
+ if (te > isect) {
+ isect = te;
+ }
+ }
+ else if (this.pos.x < low.x || this.pos.x > high.x)
+ {
+ return false;
+ }
+
+ if (this.dir.y > x3dom.fields.Eps)
+ {
+ r = 1.0 / this.dir.y;
+
+ te = (low.y - this.pos.y) * r;
+ tl = (high.y - this.pos.y) * r;
+
+ if (tl < out){
+ out = tl;
+ }
+
+ if (te > isect) {
+ isect = te;
+ }
+
+ if (isect-out >= x3dom.fields.Eps) {
+ return false;
+ }
+ }
+ else if (this.dir.y < -x3dom.fields.Eps)
+ {
+ r = 1.0 / this.dir.y;
+
+ te = (high.y - this.pos.y) * r;
+ tl = (low.y - this.pos.y) * r;
+
+ if (tl < out){
+ out = tl;
+ }
+
+ if (te > isect) {
+ isect = te;
+ }
+
+ if (isect-out >= x3dom.fields.Eps) {
+ return false;
+ }
+ }
+ else if (this.pos.y < low.y || this.pos.y > high.y)
+ {
+ return false;
+ }
+
+ if (this.dir.z > x3dom.fields.Eps)
+ {
+ r = 1.0 / this.dir.z;
+
+ te = (low.z - this.pos.z) * r;
+ tl = (high.z - this.pos.z) * r;
+
+ if (tl < out) {
+ out = tl;
+ }
+
+ if (te > isect) {
+ isect = te;
+ }
+ }
+ else if (this.dir.z < -x3dom.fields.Eps)
+ {
+ r = 1.0 / this.dir.z;
+
+ te = (high.z - this.pos.z) * r;
+ tl = (low.z - this.pos.z) * r;
+
+ if (tl < out) {
+ out = tl;
+ }
+
+ if (te > isect) {
+ isect = te;
+ }
+ }
+ else if (this.pos.z < low.z || this.pos.z > high.z)
+ {
+ return false;
+ }
+
+ this.enter = isect;
+ this.exit = out;
+
+ return (isect-out < x3dom.fields.Eps);
+};
+
+
+///////////////////////////////////////////////////////////////////////////////
+/** BoxVolume constructor.
+ @class Represents a box volume (as internal helper).
+ */
+x3dom.fields.BoxVolume = function(min, max)
+{
+ if (arguments.length < 2) {
+ this.min = new x3dom.fields.SFVec3f(0, 0, 0);
+ this.max = new x3dom.fields.SFVec3f(0, 0, 0);
+ this.valid = false;
+ }
+ else {
+ // compiler enforced type check for min/max would be nice
+ this.min = x3dom.fields.SFVec3f.copy(min);
+ this.max = x3dom.fields.SFVec3f.copy(max);
+ this.valid = true;
+ }
+
+ this.updateInternals();
+};
+
+x3dom.fields.BoxVolume.prototype.getScalarValue = function()
+{
+ var extent = this.max.subtract(this.min);
+
+ return (extent.x*extent.y*extent.z);
+};
+
+x3dom.fields.BoxVolume.copy = function(other)
+{
+ return new x3dom.fields.BoxVolume(other.min, other.max);
+};
+
+x3dom.fields.BoxVolume.prototype.updateInternals = function()
+{
+ this.radialVec = this.max.subtract(this.min).multiply(0.5);
+ this.center = this.min.add(this.radialVec);
+ this.diameter = 2 * this.radialVec.length();
+};
+
+x3dom.fields.BoxVolume.prototype.setBounds = function(min, max)
+{
+ this.min.setValues(min);
+ this.max.setValues(max);
+
+ this.updateInternals();
+ this.valid = true;
+};
+
+x3dom.fields.BoxVolume.prototype.setBoundsByCenterSize = function(center, size)
+{
+ var halfSize = size.multiply(0.5);
+ this.min = center.subtract(halfSize);
+ this.max = center.add(halfSize);
+
+ this.updateInternals();
+ this.valid = true;
+};
+
+x3dom.fields.BoxVolume.prototype.extendBounds = function(min, max)
+{
+ if (this.valid)
+ {
+ if (this.min.x > min.x) { this.min.x = min.x; }
+ if (this.min.y > min.y) { this.min.y = min.y; }
+ if (this.min.z > min.z) { this.min.z = min.z; }
+
+ if (this.max.x < max.x) { this.max.x = max.x; }
+ if (this.max.y < max.y) { this.max.y = max.y; }
+ if (this.max.z < max.z) { this.max.z = max.z; }
+
+ this.updateInternals();
+ }
+ else
+ {
+ this.setBounds(min, max);
+ }
+};
+
+x3dom.fields.BoxVolume.prototype.getBounds = function(min, max)
+{
+ min.setValues(this.min);
+ max.setValues(this.max);
+};
+
+x3dom.fields.BoxVolume.prototype.getRadialVec = function()
+{
+ return this.radialVec;
+};
+
+x3dom.fields.BoxVolume.prototype.invalidate = function()
+{
+ this.valid = false;
+ this.min = new x3dom.fields.SFVec3f(0, 0, 0);
+ this.max = new x3dom.fields.SFVec3f(0, 0, 0);
+};
+
+x3dom.fields.BoxVolume.prototype.isValid = function()
+{
+ return this.valid;
+};
+
+x3dom.fields.BoxVolume.prototype.getCenter = function()
+{
+ return this.center;
+};
+
+x3dom.fields.BoxVolume.prototype.getDiameter = function()
+{
+ return this.diameter;
+};
+
+x3dom.fields.BoxVolume.prototype.transform = function(m)
+{
+ var xmin, ymin, zmin;
+ var xmax, ymax, zmax;
+
+ xmin = xmax = m._03;
+ ymin = ymax = m._13;
+ zmin = zmax = m._23;
+
+ // calculate xmin and xmax of new transformed BBox
+ var a = this.max.x * m._00;
+ var b = this.min.x * m._00;
+
+ if (a >= b) {
+ xmax += a;
+ xmin += b;
+ }
+ else {
+ xmax += b;
+ xmin += a;
+ }
+
+ a = this.max.y * m._01;
+ b = this.min.y * m._01;
+
+ if (a >= b) {
+ xmax += a;
+ xmin += b;
+ }
+ else {
+ xmax += b;
+ xmin += a;
+ }
+
+ a = this.max.z * m._02;
+ b = this.min.z * m._02;
+
+ if (a >= b) {
+ xmax += a;
+ xmin += b;
+ }
+ else {
+ xmax += b;
+ xmin += a;
+ }
+
+ // calculate ymin and ymax of new transformed BBox
+ a = this.max.x * m._10;
+ b = this.min.x * m._10;
+
+ if (a >= b) {
+ ymax += a;
+ ymin += b;
+ }
+ else {
+ ymax += b;
+ ymin += a;
+ }
+
+ a = this.max.y * m._11;
+ b = this.min.y * m._11;
+
+ if (a >= b) {
+ ymax += a;
+ ymin += b;
+ }
+ else {
+ ymax += b;
+ ymin += a;
+ }
+
+ a = this.max.z * m._12;
+ b = this.min.z * m._12;
+
+ if (a >= b) {
+ ymax += a;
+ ymin += b;
+ }
+ else {
+ ymax += b;
+ ymin += a;
+ }
+
+ // calculate zmin and zmax of new transformed BBox
+ a = this.max.x * m._20;
+ b = this.min.x * m._20;
+
+ if (a >= b) {
+ zmax += a;
+ zmin += b;
+ }
+ else {
+ zmax += b;
+ zmin += a;
+ }
+
+ a = this.max.y * m._21;
+ b = this.min.y * m._21;
+
+ if (a >= b) {
+ zmax += a;
+ zmin += b;
+ }
+ else {
+ zmax += b;
+ zmin += a;
+ }
+
+ a = this.max.z * m._22;
+ b = this.min.z * m._22;
+
+ if (a >= b) {
+ zmax += a;
+ zmin += b;
+ }
+ else {
+ zmax += b;
+ zmin += a;
+ }
+
+ this.min.x = xmin;
+ this.min.y = ymin;
+ this.min.z = zmin;
+
+ this.max.x = xmax;
+ this.max.y = ymax;
+ this.max.z = zmax;
+
+ this.updateInternals();
+};
+
+x3dom.fields.BoxVolume.prototype.transformFrom = function(m, other)
+{
+ var xmin, ymin, zmin;
+ var xmax, ymax, zmax;
+
+ xmin = xmax = m._03;
+ ymin = ymax = m._13;
+ zmin = zmax = m._23;
+
+ // calculate xmin and xmax of new transformed BBox
+ var a = other.max.x * m._00;
+ var b = other.min.x * m._00;
+
+ if (a >= b) {
+ xmax += a;
+ xmin += b;
+ }
+ else {
+ xmax += b;
+ xmin += a;
+ }
+
+ a = other.max.y * m._01;
+ b = other.min.y * m._01;
+
+ if (a >= b) {
+ xmax += a;
+ xmin += b;
+ }
+ else {
+ xmax += b;
+ xmin += a;
+ }
+
+ a = other.max.z * m._02;
+ b = other.min.z * m._02;
+
+ if (a >= b) {
+ xmax += a;
+ xmin += b;
+ }
+ else {
+ xmax += b;
+ xmin += a;
+ }
+
+ // calculate ymin and ymax of new transformed BBox
+ a = other.max.x * m._10;
+ b = other.min.x * m._10;
+
+ if (a >= b) {
+ ymax += a;
+ ymin += b;
+ }
+ else {
+ ymax += b;
+ ymin += a;
+ }
+
+ a = other.max.y * m._11;
+ b = other.min.y * m._11;
+
+ if (a >= b) {
+ ymax += a;
+ ymin += b;
+ }
+ else {
+ ymax += b;
+ ymin += a;
+ }
+
+ a = other.max.z * m._12;
+ b = other.min.z * m._12;
+
+ if (a >= b) {
+ ymax += a;
+ ymin += b;
+ }
+ else {
+ ymax += b;
+ ymin += a;
+ }
+
+ // calculate zmin and zmax of new transformed BBox
+ a = other.max.x * m._20;
+ b = other.min.x * m._20;
+
+ if (a >= b) {
+ zmax += a;
+ zmin += b;
+ }
+ else {
+ zmax += b;
+ zmin += a;
+ }
+
+ a = other.max.y * m._21;
+ b = other.min.y * m._21;
+
+ if (a >= b) {
+ zmax += a;
+ zmin += b;
+ }
+ else {
+ zmax += b;
+ zmin += a;
+ }
+
+ a = other.max.z * m._22;
+ b = other.min.z * m._22;
+
+ if (a >= b) {
+ zmax += a;
+ zmin += b;
+ }
+ else {
+ zmax += b;
+ zmin += a;
+ }
+
+ this.min.x = xmin;
+ this.min.y = ymin;
+ this.min.z = zmin;
+
+ this.max.x = xmax;
+ this.max.y = ymax;
+ this.max.z = zmax;
+
+ this.updateInternals();
+ this.valid = true;
+};
+
+
+///////////////////////////////////////////////////////////////////////////////
+/** FrustumVolume constructor.
+ @class Represents a frustum (as internal helper).
+ */
+x3dom.fields.FrustumVolume = function(clipMat)
+{
+ this.planeNormals = [];
+ this.planeDistances = [];
+ this.directionIndex = [];
+
+ if (arguments.length === 0) {
+ return;
+ }
+
+ var planeEquation = [];
+
+ for (var i=0; i<6; i++) {
+ this.planeNormals[i] = new x3dom.fields.SFVec3f(0, 0, 0);
+ this.planeDistances[i] = 0;
+ this.directionIndex[i] = 0;
+
+ planeEquation[i] = new x3dom.fields.SFVec4f(0, 0, 0, 0);
+ }
+
+ planeEquation[0].x = clipMat._30 - clipMat._00;
+ planeEquation[0].y = clipMat._31 - clipMat._01;
+ planeEquation[0].z = clipMat._32 - clipMat._02;
+ planeEquation[0].w = clipMat._33 - clipMat._03;
+
+ planeEquation[1].x = clipMat._30 + clipMat._00;
+ planeEquation[1].y = clipMat._31 + clipMat._01;
+ planeEquation[1].z = clipMat._32 + clipMat._02;
+ planeEquation[1].w = clipMat._33 + clipMat._03;
+
+ planeEquation[2].x = clipMat._30 + clipMat._10;
+ planeEquation[2].y = clipMat._31 + clipMat._11;
+ planeEquation[2].z = clipMat._32 + clipMat._12;
+ planeEquation[2].w = clipMat._33 + clipMat._13;
+
+ planeEquation[3].x = clipMat._30 - clipMat._10;
+ planeEquation[3].y = clipMat._31 - clipMat._11;
+ planeEquation[3].z = clipMat._32 - clipMat._12;
+ planeEquation[3].w = clipMat._33 - clipMat._13;
+
+ planeEquation[4].x = clipMat._30 + clipMat._20;
+ planeEquation[4].y = clipMat._31 + clipMat._21;
+ planeEquation[4].z = clipMat._32 + clipMat._22;
+ planeEquation[4].w = clipMat._33 + clipMat._23;
+
+ planeEquation[5].x = clipMat._30 - clipMat._20;
+ planeEquation[5].y = clipMat._31 - clipMat._21;
+ planeEquation[5].z = clipMat._32 - clipMat._22;
+ planeEquation[5].w = clipMat._33 - clipMat._23;
+
+ for (i=0; i<6; i++) {
+ var vectorLength = Math.sqrt(planeEquation[i].x * planeEquation[i].x +
+ planeEquation[i].y * planeEquation[i].y +
+ planeEquation[i].z * planeEquation[i].z);
+
+ planeEquation[i].x /= vectorLength;
+ planeEquation[i].y /= vectorLength;
+ planeEquation[i].z /= vectorLength;
+ planeEquation[i].w /= -vectorLength;
+ }
+
+ var updateDirectionIndex = function(normalVec) {
+ var ind = 0;
+ if (normalVec.x > 0) ind |= 1;
+ if (normalVec.y > 0) ind |= 2;
+ if (normalVec.z > 0) ind |= 4;
+ return ind;
+ };
+
+ // right
+ this.planeNormals[3].setValues(planeEquation[0]);
+ this.planeDistances[3] = planeEquation[0].w;
+ this.directionIndex[3] = updateDirectionIndex(this.planeNormals[3]);
+
+ // left
+ this.planeNormals[2].setValues(planeEquation[1]);
+ this.planeDistances[2] = planeEquation[1].w;
+ this.directionIndex[2] = updateDirectionIndex(this.planeNormals[2]);
+
+ // bottom
+ this.planeNormals[5].setValues(planeEquation[2]);
+ this.planeDistances[5] = planeEquation[2].w;
+ this.directionIndex[5] = updateDirectionIndex(this.planeNormals[5]);
+
+ // top
+ this.planeNormals[4].setValues(planeEquation[3]);
+ this.planeDistances[4] = planeEquation[3].w;
+ this.directionIndex[4] = updateDirectionIndex(this.planeNormals[4]);
+
+ // near
+ this.planeNormals[0].setValues(planeEquation[4]);
+ this.planeDistances[0] = planeEquation[4].w;
+ this.directionIndex[0] = updateDirectionIndex(this.planeNormals[0]);
+
+ // far
+ this.planeNormals[1].setValues(planeEquation[5]);
+ this.planeDistances[1] = planeEquation[5].w;
+ this.directionIndex[1] = updateDirectionIndex(this.planeNormals[1]);
+};
+
+/** Check the volume against the frustum. */
+x3dom.fields.FrustumVolume.prototype.intersect = function(vol, planeMask)
+{
+ if (this.planeNormals.length < 6) {
+ x3dom.debug.logWarning("FrustumVolume not initialized!");
+ return false;
+ }
+
+ var that = this;
+ var min = vol.min, max = vol.max;
+
+ var setDirectionIndexPoint = function(index) {
+ var pnt = new x3dom.fields.SFVec3f(0, 0, 0);
+ if (index & 1) { pnt.x = min.x; }
+ else { pnt.x = max.x; }
+ if (index & 2) { pnt.y = min.y; }
+ else { pnt.y = max.y; }
+ if (index & 4) { pnt.z = min.z; }
+ else { pnt.z = max.z; }
+ return pnt;
+ };
+
+ //Check if the point is in the halfspace
+ var pntIsInHalfSpace = function(i, pnt) {
+ var s = that.planeNormals[i].dot(pnt) - that.planeDistances[i];
+ return (s >= 0);
+ };
+
+ //Check if the box formed by min/max is fully inside the halfspace
+ var isInHalfSpace = function(i) {
+ var p = setDirectionIndexPoint(that.directionIndex[i]);
+ return pntIsInHalfSpace(i, p);
+ };
+
+ //Check if the box formed by min/max is fully outside the halfspace
+ var isOutHalfSpace = function(i) {
+ var p = setDirectionIndexPoint(that.directionIndex[i] ^ 7);
+ return !pntIsInHalfSpace(i, p);
+ };
+
+ //Check each point of the box to the 6 planes
+ var mask = 1;
+ if (planeMask < 0) planeMask = 0;
+
+ for (var i=0; i<6; i++, mask<<=1) {
+ if ((planeMask & mask) != 0)
+ continue;
+
+ if (isOutHalfSpace(i))
+ return -1;
+
+ if (isInHalfSpace(i))
+ planeMask |= mask;
+ }
+
+ return planeMask;
+};
+
+/*
+ * X3DOM JavaScript Library
+ * http://www.x3dom.org
+ *
+ * (C)2009 Fraunhofer IGD, Darmstadt, Germany
+ * Dual licensed under the MIT and GPL
+ *
+ * Based on code originally provided by
+ * Philip Taylor: http://philip.html5.org
+ */
+
+
+// This module adds documentation related functionality
+// to the library.
+
+/** The x3dom.docs namespace.
+ * @namespace x3dom.docs
+ */
+x3dom.docs = {};
+
+
+x3dom.docs.specURLMap = {
+ CADGeometry: "CADGeometry.html",
+ Core: "core.html",
+ DIS: "dis.html",
+ CubeMapTexturing: "env_texture.html",
+ EnvironmentalEffects: "enveffects.html",
+ EnvironmentalSensor: "envsensor.html",
+ Followers: "followers.html",
+ Geospatial: "geodata.html",
+ Geometry2D: "geometry2D.html",
+ Geometry3D: "geometry3D.html",
+ Grouping: "group.html",
+ "H-Anim": "hanim.html",
+ Interpolation: "interp.html",
+ KeyDeviceSensor: "keyboard.html",
+ Layering: "layering.html",
+ Layout: "layout.html",
+ Lighting: "lighting.html",
+ Navigation: "navigation.html",
+ Networking: "networking.html",
+ NURBS: "nurbs.html",
+ ParticleSystems: "particle_systems.html",
+ Picking: "picking.html",
+ PointingDeviceSensor: "pointingsensor.html",
+ Rendering: "rendering.html",
+ RigidBodyPhysics: "rigid_physics.html",
+ Scripting: "scripting.html",
+ Shaders: "shaders.html",
+ Shape: "shape.html",
+ Sound: "sound.html",
+ Text: "text.html",
+ Texturing3D: "texture3D.html",
+ Texturing: "texturing.html",
+ Time: "time.html",
+ EventUtilities: "utils.html",
+ VolumeRendering: "volume.html"
+};
+
+x3dom.docs.specBaseURL = "http://www.web3d.org/x3d/specifications/ISO-IEC-19775-1.2-X3D-AbstractSpecification/Part01/components/";
+
+
+// the dump-nodetype tree functionality in a function
+x3dom.docs.getNodeTreeInfo = function() {
+
+ // Create the nodetype hierarchy
+ var tn, t;
+ var types = "";
+
+ var objInArray = function(array, obj) {
+ for(var i=0; i<array.length; i++) {
+ if (array[i] === obj) {
+ return true;
+ }
+ }
+ return false;
+ };
+
+ var dump = function(t, indent) {
+ for (var i=0; i<indent; i++) {
+ types += "&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;";
+ }
+
+ types += "<a href='" +
+ x3dom.docs.specBaseURL + x3dom.docs.specURLMap[x3dom.nodeTypes[t]._compName] + "#" + t +
+ "' style='color:black; text-decoration:none; font-weight:bold;'>" +
+ t + "</a> &nbsp; <a href='" +
+ x3dom.docs.specBaseURL + x3dom.docs.specURLMap[x3dom.nodeTypes[t]._compName] +
+ "' style='color:black; text-decoration:none; font-style:italic;'>" +
+ x3dom.nodeTypes[t]._compName + "</a><br/>";
+
+ for (var i in x3dom.nodeTypes[t].childTypes[t]) {
+ dump(x3dom.nodeTypes[t].childTypes[t][i], indent+1);
+ }
+ };
+
+ for (tn in x3dom.nodeTypes) {
+ var t = x3dom.nodeTypes[tn];
+ if (t.childTypes === undefined) {
+ t.childTypes = {};
+ }
+
+ while (t.superClass) {
+ if (t.superClass.childTypes[t.superClass._typeName] === undefined) {
+ t.superClass.childTypes[t.superClass._typeName] = [];
+ }
+ if (!objInArray(t.superClass.childTypes[t.superClass._typeName], t._typeName)) {
+ t.superClass.childTypes[t.superClass._typeName].push(t._typeName);
+ }
+ t = t.superClass;
+ }
+ }
+
+ dump("X3DNode", 0);
+
+ return "<div class='x3dom-doc-nodes-tree'>" + types + "</div>";
+};
+
+
+x3dom.docs.getComponentInfo = function() {
+ // Dump nodetypes by component
+ // but first sort alphabetically
+ var components = [];
+ var component;
+ var result = "";
+ var c, cn;
+
+ for (c in x3dom.components) {
+ components.push(c);
+ }
+ components.sort();
+
+ //for (var c in x3dom.components) {
+ for (cn in components) {
+ c = components[cn];
+ component = x3dom.components[c];
+ result += "<h2><a href='" +
+ x3dom.docs.specBaseURL + x3dom.docs.specURLMap[c] +
+ "' style='color:black; text-decoration:none; font-style:italic;'>" +
+ c + "</a></h2>";
+
+ result += "<ul style='list-style-type:circle;'>";
+
+ //var $ul = $("#components ul:last");
+ for (var t in component) {
+ result += "<li><a href='" +
+ x3dom.docs.specBaseURL + x3dom.docs.specURLMap[c] + "#" + t +
+ "' style='color:black; text-decoration:none; font-weight:bold;'>" +
+ t + "</a></li>";
+ }
+ result += "</ul>";
+ }
+
+ return result;
+};
+
+/*
+ * X3DOM JavaScript Library
+ * http://www.x3dom.org
+ *
+ * (C)2009 Fraunhofer IGD, Darmstadt, Germany
+ * Dual licensed under the MIT and GPL
+ *
+ * Based on code originally provided by
+ * Philip Taylor: http://philip.html5.org
+ */
+
+x3dom.shader = {};
+
+x3dom.shader.PICKING = "picking";
+x3dom.shader.PICKING_24 = "picking24";
+x3dom.shader.PICKING_ID = "pickingId";
+x3dom.shader.PICKING_COLOR = "pickingColor";
+x3dom.shader.PICKING_TEXCOORD = "pickingTexCoord";
+x3dom.shader.FRONTGROUND_TEXTURE = "frontgroundTexture";
+x3dom.shader.BACKGROUND_TEXTURE = "backgroundTexture";
+x3dom.shader.BACKGROUND_SKYTEXTURE = "backgroundSkyTexture";
+x3dom.shader.BACKGROUND_CUBETEXTURE = "backgroundCubeTexture";
+x3dom.shader.SHADOW = "shadow";
+x3dom.shader.BLUR = "blur";
+x3dom.shader.DEPTH = "depth";
+x3dom.shader.NORMAL = "normal";
+x3dom.shader.TEXTURE_REFINEMENT = "textureRefinement";
+x3dom.shader.SSAO = "ssao";
+
+/*
+ * X3DOM JavaScript Library
+ * http://www.x3dom.org
+ *
+ * (C)2009 Fraunhofer IGD, Darmstadt, Germany
+ * Dual licensed under the MIT and GPL
+ *
+ * Based on code originally provided by
+ * Philip Taylor: http://philip.html5.org
+ */
+
+
+/*******************************************************************************
+* Material
+********************************************************************************/
+ x3dom.shader.material = function() {
+ var shaderPart = "uniform vec3 diffuseColor;\n" +
+ "uniform vec3 specularColor;\n" +
+ "uniform vec3 emissiveColor;\n" +
+ "uniform float shininess;\n" +
+ "uniform float transparency;\n" +
+ "uniform float ambientIntensity;\n";
+
+ return shaderPart;
+};
+
+/*******************************************************************************
+ * TwoSidedMaterial
+ ********************************************************************************/
+x3dom.shader.twoSidedMaterial = function() {
+ var shaderPart = "uniform vec3 backDiffuseColor;\n" +
+ "uniform vec3 backSpecularColor;\n" +
+ "uniform vec3 backEmissiveColor;\n" +
+ "uniform float backShininess;\n" +
+ "uniform float backTransparency;\n" +
+ "uniform float backAmbientIntensity;\n";
+
+ return shaderPart;
+};
+
+/*******************************************************************************
+* Fog
+********************************************************************************/
+x3dom.shader.fog = function() {
+
+ var shaderPart = "uniform vec3 fogColor;\n" +
+ "uniform float fogType;\n" +
+ "uniform float fogRange;\n" +
+ "varying vec3 fragEyePosition;\n" +
+ "float calcFog(in vec3 eye) {\n" +
+ " float f0 = 0.0;\n" +
+ " if(fogType == 0.0) {\n" +
+ " if(length(eye) < fogRange){\n" +
+ " f0 = (fogRange-length(eye)) / fogRange;\n" +
+ " }\n" +
+ " }else{\n" +
+ " if(length(eye) < fogRange){\n" +
+ " f0 = exp(-length(eye) / (fogRange-length(eye) ) );\n" +
+ " }\n" +
+ " }\n" +
+ " f0 = clamp(f0, 0.0, 1.0);\n" +
+ " return f0;\n" +
+ "}\n";
+
+ return shaderPart;
+};
+
+/*******************************************************************************
+ * Clipplane
+ ********************************************************************************/
+x3dom.shader.clipPlanes = function(numClipPlanes) {
+ var shaderPart = "", c;
+
+ for(c=0; c<numClipPlanes; c++) {
+ shaderPart += "uniform vec4 clipPlane"+c+"_Plane;\n";
+ shaderPart += "uniform float clipPlane"+c+"_CappingStrength;\n";
+ shaderPart += "uniform vec3 clipPlane"+c+"_CappingColor;\n";
+ }
+
+ shaderPart += "vec3 calculateClipPlanes() {\n";
+
+ for(c=0; c<numClipPlanes; c++) {
+ shaderPart += " vec4 clipPlane" + c + " = clipPlane" + c + "_Plane * viewMatrixInverse;\n";
+ shaderPart += " float dist" + c + " = dot(fragPosition, clipPlane" + c + ");\n";
+ }
+
+ shaderPart += " if( ";
+
+ for(c=0; c<numClipPlanes; c++) {
+ if(c!=0) {
+ shaderPart += " || ";
+ }
+ shaderPart += "dist" + c + " < 0.0" ;
+ }
+
+ shaderPart += " ) ";
+ shaderPart += "{ discard; }\n";
+
+ for (c = 0; c < numClipPlanes; c++) {
+ shaderPart += " if( abs(dist" + c + ") < clipPlane" + c + "_CappingStrength ) ";
+ shaderPart += "{ return clipPlane" + c + "_CappingColor; }\n";
+ }
+
+ shaderPart += " return vec3(-1.0, -1.0, -1.0);\n";
+
+ shaderPart += "}\n";
+
+ return shaderPart;
+};
+
+/*******************************************************************************
+* Gamma correction support: initial declaration
+********************************************************************************/
+x3dom.shader.gammaCorrectionDecl = function(properties) {
+ var shaderPart = "";
+ if (properties.GAMMACORRECTION === "none") {
+ // do not emit any declaration. 1.0 shall behave 'as without gamma'.
+ } else if (properties.GAMMACORRECTION === "fastlinear") {
+ // This is a slightly optimized gamma correction
+ // which uses a gamma of 2.0 instead of 2.2. Gamma 2.0 is less costly
+ // to encode in terms of cycles as sqrt() is usually optimized
+ // in hardware.
+ shaderPart += "vec4 gammaEncode(vec4 color){\n" +
+ " vec4 tmp = sqrt(color);\n" +
+ " return vec4(tmp.rgb, color.a);\n" +
+ "}\n";
+
+ shaderPart += "vec4 gammaDecode(vec4 color){\n" +
+ " vec4 tmp = color * color;\n" +
+ " return vec4(tmp.rgb, color.a);\n" +
+ "}\n";
+
+ shaderPart += "vec3 gammaEncode(vec3 color){\n" +
+ " return sqrt(color);\n" +
+ "}\n";
+
+ shaderPart += "vec3 gammaDecode(vec3 color){\n" +
+ " return (color * color);\n" +
+ "}\n";
+ } else {
+ // The preferred implementation compensating for a gamma of 2.2, which closely
+ // follows sRGB; alpha remains linear
+ // minor opt: 1.0 / 2.2 = 0.4545454545454545
+ shaderPart += "const vec4 gammaEncode4Vector = vec4(0.4545454545454545, 0.4545454545454545, 0.4545454545454545, 1.0);\n";
+ shaderPart += "const vec4 gammaDecode4Vector = vec4(2.2, 2.2, 2.2, 1.0);\n";
+
+ shaderPart += "vec4 gammaEncode(vec4 color){\n" +
+ " return pow(color, gammaEncode4Vector);\n" +
+ "}\n";
+
+ shaderPart += "vec4 gammaDecode(vec4 color){\n" +
+ " return pow(color, gammaDecode4Vector);\n" +
+ "}\n";
+
+ // RGB; minor opt: 1.0 / 2.2 = 0.4545454545454545
+ shaderPart += "const vec3 gammaEncode3Vector = vec3(0.4545454545454545, 0.4545454545454545, 0.4545454545454545);\n";
+ shaderPart += "const vec3 gammaDecode3Vector = vec3(2.2, 2.2, 2.2);\n";
+
+ shaderPart += "vec3 gammaEncode(vec3 color){\n" +
+ " return pow(color, gammaEncode3Vector);\n" +
+ "}\n";
+
+ shaderPart += "vec3 gammaDecode(vec3 color){\n" +
+ " return pow(color, gammaDecode3Vector);\n" +
+ "}\n";
+ }
+ return shaderPart;
+};
+
+/*******************************************************************************
+* Gamma correction support: encoding and decoding of given expressions
+*
+* Unlike other shader parts these javascript functions wrap the same-named gamma
+* correction shader functions (if applicable). When gamma correction is not used,
+* the expression will be returned verbatim. Consequently, any terminating semicolon
+* is to be issued by the caller.
+********************************************************************************/
+x3dom.shader.encodeGamma = function(properties, expr) {
+ if (properties.GAMMACORRECTION === "none") {
+ // Naive implementation: no-op, return verbatim
+ return expr;
+ } else {
+ // The 2.0 and 2.2 cases are transparent at the call site
+ return "gammaEncode (" + expr + ")";
+ }
+};
+
+x3dom.shader.decodeGamma = function(properties, expr) {
+ if (properties.GAMMACORRECTION === "none") {
+ // Naive implementation: no-op, return verbatim
+ return expr;
+ } else {
+ // The 2.0 and 2.2 cases are transparent at the call site
+ return "gammaDecode (" + expr + ")";
+ }
+};
+
+/*******************************************************************************
+* Shadow
+********************************************************************************/
+x3dom.shader.rgbaPacking = function() {
+ var shaderPart = "";
+ shaderPart +=
+ "vec4 packDepth(float depth){\n" +
+ " depth = (depth + 1.0)*0.5;\n" +
+ " vec4 outVal = vec4(1.0, 255.0, 65025.0, 160581375.0) * depth;\n" +
+ " outVal = fract(outVal);\n" +
+ " outVal -= outVal.yzww * vec4(1.0/255.0, 1.0/255.0, 1.0/255.0, 0.0);\n" +
+ " return outVal;\n" +
+ "}\n";
+
+ shaderPart +=
+ "float unpackDepth(vec4 color){\n" +
+ " float depth = dot(color, vec4(1.0, 1.0/255.0, 1.0/65025.0, 1.0/160581375.0));\n" +
+ " return (2.0*depth - 1.0);\n" +
+ "}\n";
+ return shaderPart;
+};
+
+x3dom.shader.shadowRendering = function(){
+ //determine if and how much a given position is influenced by given light
+ var shaderPart = "";
+ shaderPart +=
+ "float getLightInfluence(float lType, float lShadowIntensity, float lOn, vec3 lLocation, vec3 lDirection, " +
+ "float lCutOffAngle, float lBeamWidth, vec3 lAttenuation, float lRadius, vec3 eyeCoords) {\n" +
+ " if (lOn == 0.0 || lShadowIntensity == 0.0){ return 0.0;\n" +
+ " } else if (lType == 0.0) {\n" +
+ " return 1.0;\n" +
+ " } else {\n" +
+ " float attenuation = 0.0;\n" +
+ " vec3 lightVec = (lLocation - (eyeCoords));\n" +
+ " float distance = length(lightVec);\n" +
+ " lightVec = normalize(lightVec);\n" +
+ " eyeCoords = normalize(-eyeCoords);\n" +
+ " if(lRadius == 0.0 || distance <= lRadius) {\n" +
+ " attenuation = 1.0 / max(lAttenuation.x + lAttenuation.y * distance + lAttenuation.z * (distance * distance), 1.0);\n" +
+ " }\n" +
+ " if (lType == 1.0) return attenuation;\n" +
+ " float spotAngle = acos(max(0.0, dot(-lightVec, normalize(lDirection))));\n" +
+ " if(spotAngle >= lCutOffAngle) return 0.0;\n" +
+ " else if(spotAngle <= lBeamWidth) return attenuation;\n" +
+ " else return attenuation * (spotAngle - lCutOffAngle) / (lBeamWidth - lCutOffAngle);\n" +
+ " }\n" +
+ "}\n";
+
+ // get light space depth of view sample and all entries of the shadow map
+ shaderPart +=
+ "void getShadowValues(inout vec4 shadowMapValues, inout float viewSampleDepth, in mat4 lightMatrix, in vec4 worldCoords, in sampler2D shadowMap){\n" +
+ " vec4 lightSpaceCoords = lightMatrix*worldCoords;\n" +
+ " vec3 lightSpaceCoordsCart = lightSpaceCoords.xyz / lightSpaceCoords.w;\n" +
+ " vec2 textureCoords = (lightSpaceCoordsCart.xy + 1.0)*0.5;\n" +
+ " viewSampleDepth = lightSpaceCoordsCart.z;\n" +
+ " shadowMapValues = texture2D(shadowMap, textureCoords);\n";
+ if (!x3dom.caps.FP_TEXTURES || x3dom.caps.MOBILE)
+ shaderPart += " shadowMapValues = vec4(1.0,1.0,unpackDepth(shadowMapValues),1.0);\n";
+ shaderPart +="}\n";
+
+
+ // get light space depth of view sample and all entries of the shadow map for point lights
+ shaderPart +=
+ "void getShadowValuesPointLight(inout vec4 shadowMapValues, inout float viewSampleDepth, in vec3 lLocation, in vec4 worldCoords, in mat4 lightViewMatrix," +
+ "in mat4 lMatrix_0, in mat4 lMatrix_1, in mat4 lMatrix_2, in mat4 lMatrix_3, in mat4 lMatrix_4, in mat4 lMatrix_5," +
+ "in sampler2D shadowMap_0, in sampler2D shadowMap_1, in sampler2D shadowMap_2, in sampler2D shadowMap_3,"+
+ "in sampler2D shadowMap_4, in sampler2D shadowMap_5){\n" +
+ " vec4 transformed = lightViewMatrix * worldCoords;\n" +
+ " vec3 lightVec = normalize(transformed.xyz/transformed.w);\n"+
+ " vec3 lightVecAbs = abs(lightVec);\n" +
+ " float maximum = max(max(lightVecAbs.x, lightVecAbs.y),lightVecAbs.z);\n" +
+ " if (lightVecAbs.x == maximum) {\n" +
+ " if (lightVec.x < 0.0) getShadowValues(shadowMapValues, viewSampleDepth, lMatrix_3,worldCoords,shadowMap_3);\n"+ //right
+ " else getShadowValues(shadowMapValues, viewSampleDepth, lMatrix_1,worldCoords,shadowMap_1);\n" + //left
+ " }\n" +
+ " else if (lightVecAbs.y == maximum) {\n" +
+ " if (lightVec.y < 0.0) getShadowValues(shadowMapValues, viewSampleDepth, lMatrix_4,worldCoords,shadowMap_4);\n"+ //front
+ " else getShadowValues(shadowMapValues, viewSampleDepth, lMatrix_5,worldCoords,shadowMap_5);\n" + //back
+ " }\n" +
+ " else if (lightVec.z < 0.0) getShadowValues(shadowMapValues, viewSampleDepth, lMatrix_0,worldCoords,shadowMap_0);\n"+ //bottom
+ " else getShadowValues(shadowMapValues, viewSampleDepth, lMatrix_2,worldCoords,shadowMap_2);\n" + //top
+ "}\n";
+
+ // get light space depth of view sample and all entries of the shadow map
+ shaderPart +=
+ "void getShadowValuesCascaded(inout vec4 shadowMapValues, inout float viewSampleDepth, in vec4 worldCoords, in float eyeDepth, in mat4 lMatrix_0, in mat4 lMatrix_1, in mat4 lMatrix_2,"+
+ "in mat4 lMatrix_3, in mat4 lMatrix_4, in mat4 lMatrix_5, in sampler2D shadowMap_0, in sampler2D shadowMap_1, in sampler2D shadowMap_2,"+
+ "in sampler2D shadowMap_3, in sampler2D shadowMap_4, in sampler2D shadowMap_5, in float split_0, in float split_1, in float split_2, in float split_3, in float split_4){\n" +
+ " if (eyeDepth < split_0) getShadowValues(shadowMapValues, viewSampleDepth, lMatrix_0, worldCoords, shadowMap_0);\n" +
+ " else if (eyeDepth < split_1) getShadowValues(shadowMapValues, viewSampleDepth, lMatrix_1, worldCoords, shadowMap_1);\n" +
+ " else if (eyeDepth < split_2) getShadowValues(shadowMapValues, viewSampleDepth, lMatrix_2, worldCoords, shadowMap_2);\n" +
+ " else if (eyeDepth < split_3) getShadowValues(shadowMapValues, viewSampleDepth, lMatrix_3, worldCoords, shadowMap_3);\n" +
+ " else if (eyeDepth < split_4) getShadowValues(shadowMapValues, viewSampleDepth, lMatrix_4, worldCoords, shadowMap_4);\n" +
+ " else getShadowValues(shadowMapValues, viewSampleDepth, lMatrix_5, worldCoords, shadowMap_5);\n" +
+ "}\n";
+
+ shaderPart +=
+ "float ESM(float shadowMapDepth, float viewSampleDepth, float offset){\n";
+ if (!x3dom.caps.FP_TEXTURES || x3dom.caps.MOBILE)
+ shaderPart += " return exp(-80.0*(1.0-offset)*(viewSampleDepth - shadowMapDepth));\n";
+ else shaderPart += " return shadowMapDepth * exp(-80.0*(1.0-offset)*viewSampleDepth);\n";
+ shaderPart +="}\n";
+
+
+ shaderPart +=
+ "float VSM(vec2 moments, float viewSampleDepth, float offset){\n"+
+ " viewSampleDepth = (viewSampleDepth + 1.0) * 0.5;\n" +
+ " if (viewSampleDepth <= moments.x) return 1.0;\n" +
+ " float variance = moments.y - moments.x * moments.x;\n" +
+ " variance = max(variance, 0.00002 + offset*0.01);\n" +
+ " float d = viewSampleDepth - moments.x;\n" +
+ " return variance/(variance + d*d);\n" +
+ "}\n";
+
+
+ return shaderPart;
+};
+
+
+/*******************************************************************************
+* Light
+********************************************************************************/
+x3dom.shader.light = function(numLights) {
+
+ var shaderPart = "";
+
+ for(var l=0; l<numLights; l++) {
+ shaderPart += "uniform float light"+l+"_On;\n" +
+ "uniform float light"+l+"_Type;\n" +
+ "uniform vec3 light"+l+"_Location;\n" +
+ "uniform vec3 light"+l+"_Direction;\n" +
+ "uniform vec3 light"+l+"_Color;\n" +
+ "uniform vec3 light"+l+"_Attenuation;\n" +
+ "uniform float light"+l+"_Radius;\n" +
+ "uniform float light"+l+"_Intensity;\n" +
+ "uniform float light"+l+"_AmbientIntensity;\n" +
+ "uniform float light"+l+"_BeamWidth;\n" +
+ "uniform float light"+l+"_CutOffAngle;\n" +
+ "uniform float light"+l+"_ShadowIntensity;\n";
+ }
+
+ shaderPart += "vec3 lighting(in float lType, in vec3 lLocation, in vec3 lDirection, in vec3 lColor, in vec3 lAttenuation, " +
+ "in float lRadius, in float lIntensity, in float lAmbientIntensity, in float lBeamWidth, " +
+ "in float lCutOffAngle, in vec3 N, in vec3 V, float shin, float ambIntensity)\n" +
+ "{\n" +
+ " vec3 L;\n" +
+ " float spot = 1.0, attentuation = 0.0;\n" +
+ " if(lType == 0.0) {\n" +
+ " L = -normalize(lDirection);\n" +
+ " V = normalize(V);\n" +
+ " attentuation = 1.0;\n" +
+ " } else{\n" +
+ " L = (lLocation - (-V));\n" +
+ " float d = length(L);\n" +
+ " L = normalize(L);\n" +
+ " V = normalize(V);\n" +
+ " if(lRadius == 0.0 || d <= lRadius) {\n" +
+ " attentuation = 1.0 / max(lAttenuation.x + lAttenuation.y * d + lAttenuation.z * (d * d), 1.0);\n" +
+ " }\n" +
+ " if(lType == 2.0) {\n" +
+ " float spotAngle = acos(max(0.0, dot(-L, normalize(lDirection))));\n" +
+ " if(spotAngle >= lCutOffAngle) spot = 0.0;\n" +
+ " else if(spotAngle <= lBeamWidth) spot = 1.0;\n" +
+ " else spot = (spotAngle - lCutOffAngle ) / (lBeamWidth - lCutOffAngle);\n" +
+ " }\n" +
+ " }\n" +
+
+ " vec3 H = normalize( L + V );\n" +
+ " float NdotL = clamp(dot(L, N), 0.0, 1.0);\n" +
+ " float NdotH = clamp(dot(H, N), 0.0, 1.0);\n" +
+
+ " float ambientFactor = lAmbientIntensity * ambIntensity;\n" +
+ " float diffuseFactor = lIntensity * NdotL;\n" +
+ " float specularFactor = lIntensity * pow(NdotH, shin*128.0);\n" +
+ " return vec3(ambientFactor, diffuseFactor, specularFactor) * attentuation * spot;\n" +
+ //" ambient += lColor * ambientFactor * attentuation * spot;\n" +
+ //" diffuse += lColor * diffuseFactor * attentuation * spot;\n" +
+ //" specular += lColor * specularFactor * attentuation * spot;\n" +
+ "}\n";
+
+ return shaderPart;
+};
+
+/*******************************************************************************
+ * cotangent_frame
+ ********************************************************************************/
+x3dom.shader.TBNCalculation = function() {
+ var shaderPart = "";
+
+ shaderPart += "mat3 cotangent_frame(vec3 N, vec3 p, vec2 uv)\n" +
+ "{\n" +
+ " // get edge vectors of the pixel triangle\n" +
+ " vec3 dp1 = dFdx( p );\n" +
+ " vec3 dp2 = dFdy( p );\n" +
+ " vec2 duv1 = dFdx( uv );\n" +
+ " vec2 duv2 = dFdy( uv );\n" +
+ "\n" +
+ " // solve the linear system\n" +
+ " vec3 dp2perp = cross( dp2, N );\n" +
+ " vec3 dp1perp = cross( N, dp1 );\n" +
+ " vec3 T = dp2perp * duv1.x + dp1perp * duv2.x;\n" +
+ " vec3 B = dp2perp * duv1.y + dp1perp * duv2.y;\n" +
+ "\n" +
+ " // construct a scale-invariant frame\n" +
+ " float invmax = inversesqrt( max( dot(T,T), dot(B,B) ) );\n" +
+ " return mat3( T * invmax, B * invmax, N );\n" +
+ "}\n\n";
+
+ shaderPart += "vec3 perturb_normal( vec3 N, vec3 V, vec2 texcoord )\n" +
+ "{\n" +
+ " // assume N, the interpolated vertex normal and\n" +
+ " // V, the view vector (vertex to eye)\n" +
+ " vec3 map = texture2D(normalMap, texcoord ).xyz;\n" +
+ " map = map * 255./127. - 128./127.;\n" +
+ " mat3 TBN = cotangent_frame(N, -V, texcoord);\n" +
+ " return normalize(TBN * map);\n" +
+ "}\n\n";
+
+ return shaderPart;
+};
+
+/*
+ * X3DOM JavaScript Library
+ * http://www.x3dom.org
+ *
+ * (C)2009 Fraunhofer IGD, Darmstadt, Germany
+ * Dual licensed under the MIT and GPL
+ *
+ * Based on code originally provided by
+ * Philip Taylor: http://philip.html5.org
+ */
+
+/**
+ * Generate the final Shader program
+ */
+x3dom.shader.DynamicShader = function(gl, properties)
+{
+ this.program = gl.createProgram();
+
+ var vertexShader = this.generateVertexShader(gl, properties);
+ var fragmentShader = this.generateFragmentShader(gl, properties);
+
+ gl.attachShader(this.program, vertexShader);
+ gl.attachShader(this.program, fragmentShader);
+
+ // optional, but position should be at location 0 for performance reasons
+ gl.bindAttribLocation(this.program, 0, "position");
+
+ gl.linkProgram(this.program);
+
+ return this.program;
+};
+
+/**
+ * Generate the vertex shader
+ */
+x3dom.shader.DynamicShader.prototype.generateVertexShader = function(gl, properties)
+{
+ var shader = "";
+
+ /*******************************************************************************
+ * Generate dynamic attributes & uniforms & varyings
+ ********************************************************************************/
+
+ //Default Matrices
+ shader += "uniform mat4 modelViewMatrix;\n";
+ shader += "uniform mat4 modelViewProjectionMatrix;\n";
+
+ //Positions
+ if(properties.POSCOMPONENTS == 3) {
+ shader += "attribute vec3 position;\n";
+ } else if(properties.POSCOMPONENTS == 4) {
+ shader += "attribute vec4 position;\n";
+ }
+
+ //IG stuff
+ if(properties.IMAGEGEOMETRY) {
+ shader += "uniform vec3 IG_bboxMin;\n";
+ shader += "uniform vec3 IG_bboxMax;\n";
+ shader += "uniform float IG_coordTextureWidth;\n";
+ shader += "uniform float IG_coordTextureHeight;\n";
+ shader += "uniform vec2 IG_implicitMeshSize;\n";
+
+ for( var i = 0; i < properties.IG_PRECISION; i++ ) {
+ shader += "uniform sampler2D IG_coords" + i + "\n;";
+ }
+
+ if(properties.IG_INDEXED) {
+ shader += "uniform sampler2D IG_index;\n";
+ shader += "uniform float IG_indexTextureWidth;\n";
+ shader += "uniform float IG_indexTextureHeight;\n";
+ }
+ }
+
+ //PG stuff
+ if (properties.POPGEOMETRY) {
+ shader += "uniform float PG_precisionLevel;\n";
+ shader += "uniform float PG_powPrecision;\n";
+ shader += "uniform vec3 PG_maxBBSize;\n";
+ shader += "uniform vec3 PG_bbMin;\n";
+ shader += "uniform vec3 PG_bbMaxModF;\n";
+ shader += "uniform vec3 PG_bboxShiftVec;\n";
+ shader += "uniform float PG_numAnchorVertices;\n";
+ shader += "attribute float PG_vertexID;\n";
+ }
+
+ //Normals
+ if(properties.LIGHTS) {
+ shader += "varying vec3 fragNormal;\n";
+ shader += "uniform mat4 normalMatrix;\n";
+ if(properties.IMAGEGEOMETRY) {
+ shader += "uniform sampler2D IG_normals;\n";
+ } else {
+ if(properties.NORCOMPONENTS == 2) {
+ if(properties.POSCOMPONENTS != 4) {
+ shader += "attribute vec2 normal;\n";
+ }
+ } else if(properties.NORCOMPONENTS == 3) {
+ shader += "attribute vec3 normal;\n";
+ }
+ }
+ }
+
+ //Init Colors. In the vertex shader we do not compute any color so
+ //is is safe to ignore gamma here.
+ if(properties.VERTEXCOLOR) {
+ if(properties.IMAGEGEOMETRY) {
+ shader += "uniform sampler2D IG_colors;\n";
+ if(properties.COLCOMPONENTS == 3) {
+ shader += "varying vec3 fragColor;\n";
+ } else if(properties.COLCOMPONENTS == 4) {
+ shader += "varying vec4 fragColor;\n";
+ }
+ } else {
+ if(properties.COLCOMPONENTS == 3) {
+ shader += "attribute vec3 color;\n";
+ shader += "varying vec3 fragColor;\n";
+ } else if(properties.COLCOMPONENTS == 4) {
+ shader += "attribute vec4 color;\n";
+ shader += "varying vec4 fragColor;\n";
+ }
+ }
+ }
+
+ //Textures
+ if(properties.TEXTURED || properties.CSSHADER) {
+ shader += "varying vec2 fragTexcoord;\n";
+ if(!properties.SPHEREMAPPING) {
+ if(properties.IMAGEGEOMETRY) {
+ shader += "uniform sampler2D IG_texCoords;\n";
+ } else if (!properties.IS_PARTICLE) {
+ shader += "attribute vec2 texcoord;\n";
+ }
+ }
+ if(properties.TEXTRAFO){
+ shader += "uniform mat4 texTrafoMatrix;\n";
+ }
+
+ if(properties.NORMALMAP && !x3dom.caps.STD_DERIVATIVES) {
+
+ x3dom.debug.logWarning("Your System doesn't support the 'OES_STANDARD_DERIVATIVES' Extension. " +
+ "You must set tangents and binormals manually via the FloatVertexAttribute-Node " +
+ "to use normal maps");
+
+ shader += "attribute vec3 tangent;\n";
+ shader += "attribute vec3 binormal;\n";
+ shader += "varying vec3 fragTangent;\n";
+ shader += "varying vec3 fragBinormal;\n";
+ }
+
+ if(properties.CUBEMAP) {
+ shader += "varying vec3 fragViewDir;\n";
+ shader += "uniform mat4 viewMatrix;\n";
+ }
+ if (properties.DISPLACEMENTMAP) {
+ shader += "uniform sampler2D displacementMap;\n";
+ shader += "uniform float displacementFactor;\n";
+ shader += "uniform float displacementWidth;\n";
+ shader += "uniform float displacementHeight;\n";
+ shader += "uniform float displacementAxis;\n";
+ }
+ if (properties.DIFFPLACEMENTMAP) {
+ shader += "uniform sampler2D diffuseDisplacementMap;\n";
+ shader += "uniform float displacementFactor;\n";
+ shader += "uniform float displacementWidth;\n";
+ shader += "uniform float displacementHeight;\n";
+ shader += "uniform float displacementAxis;\n";
+ }
+ if (properties.MULTIDIFFALPMAP || properties.MULTIVISMAP) {
+ shader += "attribute float id;\n";
+ shader += "varying float fragID;\n";
+ }
+ }
+ if (properties.IS_PARTICLE) {
+ shader += "attribute vec3 particleSize;\n";
+ }
+
+ //Lights & Fog
+ if(properties.LIGHTS || properties.FOG || properties.CLIPPLANES){
+ shader += "uniform vec3 eyePosition;\n";
+ shader += "varying vec4 fragPosition;\n";
+ if(properties.FOG) {
+ shader += "varying vec3 fragEyePosition;\n";
+ }
+ }
+
+ //Bounding Boxes
+ if(properties.REQUIREBBOX) {
+ shader += "uniform vec3 bgCenter;\n";
+ shader += "uniform vec3 bgSize;\n";
+ shader += "uniform float bgPrecisionMax;\n";
+ }
+ if(properties.REQUIREBBOXNOR) {
+ shader += "uniform float bgPrecisionNorMax;\n";
+ }
+ if(properties.REQUIREBBOXCOL) {
+ shader += "uniform float bgPrecisionColMax;\n";
+ }
+ if(properties.REQUIREBBOXTEX) {
+ shader += "uniform float bgPrecisionTexMax;\n";
+ }
+
+
+ /*******************************************************************************
+ * Generate main function
+ ********************************************************************************/
+ shader += "void main(void) {\n";
+
+ /*******************************************************************************
+ * Start of special Geometry switch
+ ********************************************************************************/
+ if(properties.IMAGEGEOMETRY) {
+ //Indices
+ if(properties.IG_INDEXED) {
+ shader += "vec2 halfPixel = vec2(0.5/IG_indexTextureWidth,0.5/IG_indexTextureHeight);\n";
+ shader += "vec2 IG_texCoord = vec2(position.x*(IG_implicitMeshSize.x/IG_indexTextureWidth), position.y*(IG_implicitMeshSize.y/IG_indexTextureHeight)) + halfPixel;\n";
+ shader += "vec2 IG_indices = texture2D( IG_index, IG_texCoord ).rg;\n";
+ shader += "halfPixel = vec2(0.5/IG_coordTextureWidth,0.5/IG_coordTextureHeight);\n";
+ shader += "IG_texCoord = (IG_indices * 0.996108948) + halfPixel;\n";
+ } else {
+ shader += "vec2 halfPixel = vec2(0.5/IG_coordTextureWidth, 0.5/IG_coordTextureHeight);\n";
+ shader += "vec2 IG_texCoord = vec2(position.x*(IG_implicitMeshSize.x/IG_coordTextureWidth), position.y*(IG_implicitMeshSize.y/IG_coordTextureHeight)) + halfPixel;\n";
+ }
+
+ //Positions
+ shader += "vec3 temp = vec3(0.0, 0.0, 0.0);\n";
+ shader += "vec3 vertPosition = vec3(0.0, 0.0, 0.0);\n";
+
+ for(var i=0; i<properties.IG_PRECISION; i++) {
+ shader += "temp = 255.0 * texture2D( IG_coords" + i + ", IG_texCoord ).rgb;\n";
+ shader += "vertPosition *= 256.0;\n";
+ shader += "vertPosition += temp;\n";
+ }
+
+ shader += "vertPosition /= (pow(2.0, 8.0 * " + properties.IG_PRECISION + ".0) - 1.0);\n";
+ shader += "vertPosition = vertPosition * (IG_bboxMax - IG_bboxMin) + IG_bboxMin;\n";
+
+ //Normals
+ if(properties.LIGHTS) {
+ shader += "vec3 vertNormal = texture2D( IG_normals, IG_texCoord ).rgb;\n";
+ shader += "vertNormal = vertNormal * 2.0 - 1.0;\n";
+ }
+
+ //Colors
+ if(properties.VERTEXCOLOR) {
+ if(properties.COLCOMPONENTS == 3) {
+ shader += "fragColor = texture2D( IG_colors, IG_texCoord ).rgb;\n";
+ } else if(properties.COLCOMPONENTS == 4) {
+ shader += "fragColor = texture2D( IG_colors, IG_texCoord ).rgba;\n";
+ }
+ }
+
+ //TexCoords
+ if(properties.TEXTURED || properties.CSSHADER) {
+ shader += "vec4 IG_doubleTexCoords = texture2D( IG_texCoords, IG_texCoord );\n";
+ shader += "vec2 vertTexCoord;";
+ shader += "vertTexCoord.r = (IG_doubleTexCoords.r * 0.996108948) + (IG_doubleTexCoords.b * 0.003891051);\n";
+ shader += "vertTexCoord.g = (IG_doubleTexCoords.g * 0.996108948) + (IG_doubleTexCoords.a * 0.003891051);\n";
+ }
+ } else {
+ //Positions
+ shader += "vec3 vertPosition = position.xyz;\n";
+
+ if (properties.POPGEOMETRY) {
+ //compute offset using bounding box and test if vertPosition <= PG_bbMaxModF
+ shader += "vec3 offsetVec = step(vertPosition / bgPrecisionMax, PG_bbMaxModF) * PG_bboxShiftVec;\n";
+
+ //coordinate truncation, computation of current maximum possible value
+ //PG_vertexID currently mimics use of gl_VertexID
+ shader += "if ((PG_precisionLevel <= 2.0) || PG_vertexID >= PG_numAnchorVertices) {\n";
+ shader += " vertPosition = floor(vertPosition / PG_powPrecision) * PG_powPrecision;\n";
+ shader += " vertPosition /= (65536.0 - PG_powPrecision);\n";
+ shader += "}\n";
+ shader += "else {\n";
+ shader += " vertPosition /= bgPrecisionMax;\n";
+ shader += "}\n";
+
+ //translate coordinates, where PG_bbMin := floor(bbMin / size)
+ shader += "vertPosition = (vertPosition + offsetVec + PG_bbMin) * PG_maxBBSize;\n";
+ }
+ else if(properties.REQUIREBBOX) {
+ shader += "vertPosition = bgCenter + bgSize * vertPosition / bgPrecisionMax;\n";
+ }
+
+ //Normals
+ if(properties.LIGHTS) {
+ if(properties.NORCOMPONENTS == 2) {
+ if (properties.POSCOMPONENTS == 4) {
+ // (theta, phi) encoded in low/high byte of position.w
+ shader += "vec3 vertNormal = vec3(position.w / 256.0); \n";
+ shader += "vertNormal.x = floor(vertNormal.x) / 255.0; \n";
+ shader += "vertNormal.y = fract(vertNormal.y) * 1.00392156862745; \n"; //256.0 / 255.0
+ }
+ else if (properties.REQUIREBBOXNOR) {
+ shader += "vec3 vertNormal = vec3(normal.xy, 0.0) / bgPrecisionNorMax;\n";
+ }
+
+ shader += "vec2 thetaPhi = 3.14159265358979 * vec2(vertNormal.x, vertNormal.y*2.0-1.0); \n";
+ shader += "vec4 sinCosThetaPhi = sin( vec4(thetaPhi, thetaPhi + 1.5707963267949) ); \n";
+
+ shader += "vertNormal.x = sinCosThetaPhi.x * sinCosThetaPhi.w; \n";
+ shader += "vertNormal.y = sinCosThetaPhi.x * sinCosThetaPhi.y; \n";
+ shader += "vertNormal.z = sinCosThetaPhi.z; \n";
+ } else {
+ shader += "vec3 vertNormal = normal;\n";
+ if (properties.REQUIREBBOXNOR) {
+ shader += "vertNormal = vertNormal / bgPrecisionNorMax;\n";
+ }
+ if (properties.POPGEOMETRY) {
+ shader += "vertNormal = 2.0*vertNormal - 1.0;\n";
+ }
+ }
+ }
+
+ //Colors
+ if(properties.VERTEXCOLOR){
+ shader += "fragColor = color;\n";
+
+ if(properties.REQUIREBBOXCOL) {
+ shader += "fragColor = fragColor / bgPrecisionColMax;\n";
+ }
+ }
+
+ //TexCoords
+ if( (properties.TEXTURED || properties.CSSHADER) && !properties.SPHEREMAPPING) {
+ if (properties.IS_PARTICLE) {
+ shader += "vec2 vertTexCoord = vec2(0.0);\n";
+ }
+ else {
+ shader += "vec2 vertTexCoord = texcoord;\n";
+ if (properties.REQUIREBBOXTEX) {
+ shader += "vertTexCoord = vertTexCoord / bgPrecisionTexMax;\n";
+ }
+ }
+ }
+ }
+
+ /*******************************************************************************
+ * End of special Geometry switch
+ ********************************************************************************/
+
+
+ //Normals
+ if(properties.LIGHTS) {
+ if (properties.DISPLACEMENTMAP || properties.DIFFPLACEMENTMAP && !properties.NORMALMAP) {
+ //Map-Tile Size
+ shader += "float dx = 1.0 / displacementWidth;\n";
+ shader += "float dy = 1.0 / displacementHeight;\n";
+
+ //Get the 4 Vertex Neighbours
+ if (properties.DISPLACEMENTMAP)
+ {
+ shader += "float s1 = texture2D(displacementMap, vec2(vertTexCoord.x - dx, 1.0 - vertTexCoord.y)).r;\n"; //left
+ shader += "float s2 = texture2D(displacementMap, vec2(vertTexCoord.x, 1.0 - vertTexCoord.y - dy)).r;\n"; //bottom
+ shader += "float s3 = texture2D(displacementMap, vec2(vertTexCoord.x + dx, 1.0 - vertTexCoord.y)).r;\n"; //right
+ shader += "float s4 = texture2D(displacementMap, vec2(vertTexCoord.x, 1.0 - vertTexCoord.y + dy)).r;\n"; //top
+ }
+ else if (properties.DIFFPLACEMENTMAP)
+ {
+ shader += "float s1 = texture2D(diffuseDisplacementMap, vec2(vertTexCoord.x - dx, 1.0 - vertTexCoord.y)).a;\n"; //left
+ shader += "float s2 = texture2D(diffuseDisplacementMap, vec2(vertTexCoord.x, 1.0 - vertTexCoord.y - dy)).a;\n"; //bottom
+ shader += "float s3 = texture2D(diffuseDisplacementMap, vec2(vertTexCoord.x + dx, 1.0 - vertTexCoord.y)).a;\n"; //right
+ shader += "float s4 = texture2D(diffuseDisplacementMap, vec2(vertTexCoord.x, 1.0 - vertTexCoord.y + dy)).a;\n"; //top
+ }
+
+ //Coeffiecent for smooth/sharp Normals
+ shader += "float coef = displacementFactor;\n";
+
+ //Calculate the Normal
+ shader += "vec3 calcNormal;\n";
+
+ shader += "if (displacementAxis == 0.0) {\n"; //X
+ shader += "calcNormal = vec3((s1 - s3) * coef, -5.0, (s2 - s4) * coef);\n";
+ shader += "} else if(displacementAxis == 1.0) {\n"; //Y
+ shader += "calcNormal = vec3((s1 - s3) * coef, -5.0, (s2 - s4) * coef);\n";
+ shader += "} else {\n"; //Z
+ shader += "calcNormal = vec3((s1 - s3) * coef, -(s2 - s4) * coef, 5.0);\n";
+ shader += "}\n";
+
+
+ //normalized Normal
+ shader += "calcNormal = normalize(calcNormal);\n";
+ shader += "fragNormal = (normalMatrix * vec4(calcNormal, 0.0)).xyz;\n";
+ }
+ else
+ {
+ shader += "fragNormal = (normalMatrix * vec4(vertNormal, 0.0)).xyz;\n";
+ }
+ }
+
+ //Textures
+ if(properties.TEXTURED || properties.CSSHADER){
+ if(properties.CUBEMAP) {
+ shader += "fragViewDir = (viewMatrix[3].xyz);\n";
+ } else if (properties.SPHEREMAPPING) {
+ shader += " fragTexcoord = 0.5 + fragNormal.xy / 2.0;\n";
+ } else if(properties.TEXTRAFO) {
+ shader += " fragTexcoord = (texTrafoMatrix * vec4(vertTexCoord, 1.0, 1.0)).xy;\n";
+ } else {
+ shader += " fragTexcoord = vertTexCoord;\n";
+
+ // LOD LUT HACK ###
+ if (properties.POPGEOMETRY && x3dom.debug.usePrecisionLevelAsTexCoord === true)
+ // remap texCoords to texel middle with w = 16 and tc' := 1 / (2 * w) + tc * (w - 1) / w
+ shader += "fragTexcoord = vec2(0.03125 + 0.9375 * (PG_precisionLevel / 16.0), 1.0);";
+ // LOD LUT HACK ###
+ }
+
+ if(properties.NORMALMAP && !x3dom.caps.STD_DERIVATIVES) {
+ shader += "fragTangent = (normalMatrix * vec4(tangent, 0.0)).xyz;\n";
+ shader += "fragBinormal = (normalMatrix * vec4(binormal, 0.0)).xyz;\n";
+ }
+ }
+
+ //Lights & Fog
+ if(properties.LIGHTS || properties.FOG || properties.CLIPPLANES){
+ shader += "fragPosition = (modelViewMatrix * vec4(vertPosition, 1.0));\n";
+ if (properties.FOG) {
+ shader += "fragEyePosition = eyePosition - fragPosition.xyz;\n";
+ }
+ }
+
+ //Vertex ID's
+ if (properties.MULTIDIFFALPMAP) {
+ shader += "fragID = id;\n";
+ }
+
+ //Displacement
+ if (properties.DISPLACEMENTMAP) {
+ shader += "vertPosition += normalize(vertNormal) * texture2D(displacementMap, vec2(fragTexcoord.x, 1.0-fragTexcoord.y)).r * displacementFactor;\n";
+ }
+ else if (properties.DIFFPLACEMENTMAP)
+ {
+ shader += "vertPosition += normalize(vertNormal) * texture2D(diffuseDisplacementMap, vec2(fragTexcoord.x, 1.0-fragTexcoord.y)).a * displacementFactor;\n";
+ }
+
+ //Positions
+ shader += "gl_Position = modelViewProjectionMatrix * vec4(vertPosition, 1.0);\n";
+
+ //Set point size
+ if (properties.IS_PARTICLE) {
+ shader += "float spriteDist = (gl_Position.w > 0.000001) ? gl_Position.w : 0.000001;\n";
+ shader += "float pointSize = floor(length(particleSize) * 256.0 / spriteDist + 0.5);\n";
+ shader += "gl_PointSize = clamp(pointSize, 2.0, 256.0);\n";
+ }
+ else {
+ shader += "gl_PointSize = 2.0;\n";
+ }
+
+ //END OF SHADER
+ shader += "}\n";
+
+ var vertexShader = gl.createShader(gl.VERTEX_SHADER);
+ gl.shaderSource(vertexShader, shader);
+ gl.compileShader(vertexShader);
+
+ if(!gl.getShaderParameter(vertexShader, gl.COMPILE_STATUS)){
+ x3dom.debug.logInfo("VERTEX:\n" + shader);
+ x3dom.debug.logError("VertexShader " + gl.getShaderInfoLog(vertexShader));
+ }
+
+ return vertexShader;
+};
+
+/**
+ * Generate the fragment shader
+ */
+x3dom.shader.DynamicShader.prototype.generateFragmentShader = function(gl, properties)
+{
+ var shader = "#ifdef GL_FRAGMENT_PRECISION_HIGH\n";
+ shader += " precision highp float;\n";
+ shader += "#else\n";
+ shader += " precision mediump float;\n";
+ shader += "#endif\n\n";
+
+ /*******************************************************************************
+ * Generate dynamic uniforms & varyings
+ ********************************************************************************/
+
+ //Default Matrices
+ shader += "uniform mat4 modelMatrix;\n";
+ shader += "uniform mat4 modelViewMatrix;\n";
+ shader += "uniform mat4 viewMatrixInverse;\n";
+
+ //Material
+ shader += x3dom.shader.material();
+
+ if (properties.TWOSIDEDMAT ) {
+ shader += x3dom.shader.twoSidedMaterial();
+ }
+
+ //Colors
+ if(properties.VERTEXCOLOR){
+ if(properties.COLCOMPONENTS == 3){
+ shader += "varying vec3 fragColor; \n";
+ }else if(properties.COLCOMPONENTS == 4){
+ shader += "varying vec4 fragColor; \n";
+ }
+ }
+
+ if(properties.CUBEMAP || properties.CLIPPLANES)
+ {
+ shader += "uniform mat4 modelViewMatrixInverse;\n";
+ }
+
+ //Textures
+ if(properties.TEXTURED || properties.CSSHADER) {
+ shader += "varying vec2 fragTexcoord;\n";
+ if((properties.TEXTURED || properties.DIFFUSEMAP) && !properties.CUBEMAP) {
+ shader += "uniform sampler2D diffuseMap;\n";
+ } else if(properties.CUBEMAP) {
+ shader += "uniform samplerCube cubeMap;\n";
+ shader += "varying vec3 fragViewDir;\n";
+
+ }
+ if(properties.SPECMAP){
+ shader += "uniform sampler2D specularMap;\n";
+ }
+ if(properties.SHINMAP){
+ shader += "uniform sampler2D shininessMap;\n";
+ }
+ if (properties.DISPLACEMENTMAP) {
+ shader += "uniform sampler2D displacementMap;\n";
+ shader += "uniform float displacementWidth;\n";
+ shader += "uniform float displacementHeight;\n";
+ }
+ if (properties.DIFFPLACEMENTMAP) {
+ shader += "uniform sampler2D diffuseDisplacementMap;\n";
+ shader += "uniform float displacementWidth;\n";
+ shader += "uniform float displacementHeight;\n";
+ }
+ if (properties.MULTIDIFFALPMAP || properties.MULTIVISMAP) {
+ shader += "varying float fragID;\n";
+ }
+ if (properties.MULTIDIFFALPMAP) {
+ shader += "uniform sampler2D multiDiffuseAlphaMap;\n";
+ shader += "uniform float multiDiffuseAlphaWidth;\n";
+ shader += "uniform float multiDiffuseAlphaHeight;\n";
+ }
+ if (properties.MULTIEMIAMBMAP) {
+ shader += "uniform sampler2D multiEmissiveAmbientMap;\n";
+ shader += "uniform float multiEmissiveAmbientWidth;\n";
+ shader += "uniform float multiEmissiveAmbientHeight;\n";
+ }
+ if (properties.MULTISPECSHINMAP) {
+ shader += "uniform sampler2D multiSpecularShininessMap;\n";
+ shader += "uniform float multiSpecularShininessWidth;\n";
+ shader += "uniform float multiSpecularShininessHeight;\n";
+ }
+ if (properties.MULTIVISMAP) {
+ shader += "uniform sampler2D multiVisibilityMap;\n";
+ shader += "uniform float multiVisibilityWidth;\n";
+ shader += "uniform float multiVisibilityHeight;\n";
+ }
+ if(properties.NORMALMAP){
+ shader += "uniform sampler2D normalMap;\n";
+
+ if(x3dom.caps.STD_DERIVATIVES) {
+ shader += "#extension GL_OES_standard_derivatives:enable\n";
+ shader += x3dom.shader.TBNCalculation();
+ } else {
+ shader += "varying vec3 fragTangent;\n";
+ shader += "varying vec3 fragBinormal;\n";
+ }
+ }
+ }
+
+ //Fog
+ if(properties.FOG) {
+ shader += x3dom.shader.fog();
+ }
+
+ if(properties.LIGHTS || properties.CLIPPLANES)
+ {
+ shader += "varying vec4 fragPosition;\n";
+ }
+
+ //Lights
+ if(properties.LIGHTS) {
+ shader += "varying vec3 fragNormal;\n";
+
+ shader += x3dom.shader.light(properties.LIGHTS);
+ }
+
+ if(properties.CLIPPLANES) {
+ shader += x3dom.shader.clipPlanes(properties.CLIPPLANES);
+ }
+
+ // Declare gamma correction for color computation (see property "GAMMACORRECTION")
+ shader += x3dom.shader.gammaCorrectionDecl(properties);
+
+
+ /*******************************************************************************
+ * Generate main function
+ ********************************************************************************/
+ shader += "void main(void) {\n";
+
+
+ if(properties.CLIPPLANES)
+ {
+ shader += "vec3 cappingColor = calculateClipPlanes();\n";
+ }
+
+ //Init color. In the fragment shader we are treating color linear by
+ //gamma-adjusting actively before doing lighting computations. At the end
+ //the color value is encoded again. See shader propery GAMMACORRECTION.
+ shader += "vec4 color;\n";
+
+ shader += "color.rgb = " + x3dom.shader.decodeGamma(properties, "diffuseColor") + ";\n";
+ shader += "color.a = 1.0 - transparency;\n";
+
+ shader += "vec3 _emissiveColor = emissiveColor;\n";
+ shader += "float _shininess = shininess;\n";
+ shader += "vec3 _specularColor = specularColor;\n";
+ shader += "float _ambientIntensity = ambientIntensity;\n";
+
+ if (properties.MULTIVISMAP || properties.MULTIDIFFALPMAP || properties.MULTISPECSHINMAP || properties.MULTIEMIAMBMAP) {
+ shader += "vec2 idCoord;\n";
+ shader += "float roundedIDVisibility = floor(fragID+0.5);\n";
+ shader += "float roundedIDMaterial = floor(fragID+0.5);\n";
+ shader += "if(!gl_FrontFacing) {\n";
+ shader += " roundedIDMaterial = floor(fragID + (multiDiffuseAlphaWidth*multiDiffuseAlphaWidth) + 0.5);\n";
+ shader += "}\n";
+ }
+
+ if (properties.MULTIVISMAP) {
+ shader += "idCoord.x = mod(roundedIDVisibility, multiVisibilityWidth) * (1.0 / multiVisibilityWidth) + (0.5 / multiVisibilityWidth);\n";
+ shader += "idCoord.y = floor(roundedIDVisibility / multiVisibilityWidth) * (1.0 / multiVisibilityHeight) + (0.5 / multiVisibilityHeight);\n";
+ shader += "vec4 visibility = texture2D( multiVisibilityMap, idCoord );\n";
+ shader += "if (visibility.r < 1.0) discard; \n";
+ }
+
+ if (properties.MULTIDIFFALPMAP) {
+ shader += "idCoord.x = mod(roundedIDMaterial, multiDiffuseAlphaWidth) * (1.0 / multiDiffuseAlphaWidth) + (0.5 / multiDiffuseAlphaWidth);\n";
+ shader += "idCoord.y = floor(roundedIDMaterial / multiDiffuseAlphaWidth) * (1.0 / multiDiffuseAlphaHeight) + (0.5 / multiDiffuseAlphaHeight);\n";
+ shader += "vec4 diffAlpha = texture2D( multiDiffuseAlphaMap, idCoord );\n";
+ shader += "color.rgb = " + x3dom.shader.decodeGamma(properties, "diffAlpha.rgb") + ";\n";
+ shader += "color.a = diffAlpha.a;\n";
+ }
+
+ if (properties.MULTIEMIAMBMAP) {
+ shader += "idCoord.x = mod(roundedIDMaterial, multiDiffuseAlphaWidth) * (1.0 / multiDiffuseAlphaWidth) + (0.5 / multiDiffuseAlphaWidth);\n";
+ shader += "idCoord.y = floor(roundedIDMaterial / multiDiffuseAlphaWidth) * (1.0 / multiDiffuseAlphaHeight) + (0.5 / multiDiffuseAlphaHeight);\n";
+ shader += "vec4 emiAmb = texture2D( multiEmissiveAmbientMap, idCoord );\n";
+ shader += "_emissiveColor = emiAmb.rgb;\n";
+ shader += "_ambientIntensity = emiAmb.a;\n";
+ }
+
+ if(properties.VERTEXCOLOR) {
+ if(properties.COLCOMPONENTS === 3){
+ shader += "color.rgb = " + x3dom.shader.decodeGamma(properties,"fragColor") + ";\n";
+ }else if(properties.COLCOMPONENTS === 4){
+ shader += "color = " + x3dom.shader.decodeGamma(properties, "fragColor") + ";\n";
+ }
+ }
+
+ if(properties.LIGHTS) {
+ shader += "vec3 ambient = vec3(0.0, 0.0, 0.0);\n";
+ shader += "vec3 diffuse = vec3(0.0, 0.0, 0.0);\n";
+ shader += "vec3 specular = vec3(0.0, 0.0, 0.0);\n";
+ shader += "vec3 normal = normalize(fragNormal);\n";
+ shader += "vec3 eye = -fragPosition.xyz;\n";
+
+
+
+ //Normalmap
+ if(properties.NORMALMAP){
+ shader += "vec3 n = normalize( fragNormal );\n";
+
+ if (x3dom.caps.STD_DERIVATIVES) {
+ shader += "normal = perturb_normal( n, fragPosition.xyz, vec2(fragTexcoord.x, 1.0-fragTexcoord.y) );\n";
+ } else {
+ shader += "vec3 t = normalize( fragTangent );\n";
+ shader += "vec3 b = normalize( fragBinormal );\n";
+ shader += "mat3 tangentToWorld = mat3(t, b, n);\n";
+
+ shader += "normal = texture2D( normalMap, vec2(fragTexcoord.x, 1.0-fragTexcoord.y) ).rgb;\n";
+ shader += "normal = 2.0 * normal - 1.0;\n";
+ shader += "normal = normalize( normal * tangentToWorld );\n";
+
+ shader += "normal.y = -normal.y;\n";
+ shader += "normal.x = -normal.x;\n";
+ }
+ }
+
+ if(properties.SHINMAP){
+ shader += "_shininess = texture2D( shininessMap, vec2(fragTexcoord.x, 1.0-fragTexcoord.y) ).r;\n";
+ }
+
+ //Specularmap
+ if(properties.SPECMAP) {
+ shader += "_specularColor = " + x3dom.shader.decodeGamma(properties, "texture2D(specularMap, vec2(fragTexcoord.x, 1.0-fragTexcoord.y)).rgb") + ";\n";
+ }
+
+ if (properties.MULTISPECSHINMAP) {
+ shader += "idCoord.x = mod(roundedIDMaterial, multiSpecularShininessWidth) * (1.0 / multiSpecularShininessWidth) + (0.5 / multiSpecularShininessWidth);\n";
+ shader += "idCoord.y = floor(roundedIDMaterial / multiSpecularShininessWidth) * (1.0 / multiSpecularShininessHeight) + (0.5 / multiSpecularShininessHeight);\n";
+ shader += "vec4 specShin = texture2D( multiSpecularShininessMap, idCoord );\n";
+ shader += "_specularColor = specShin.rgb;\n";
+ shader += "_shininess = specShin.a;\n";
+ }
+
+ //Solid
+ if(!properties.SOLID || properties.TWOSIDEDMAT) {
+ shader += "if (dot(normal, eye) < 0.0) {\n";
+ shader += " normal *= -1.0;\n";
+ shader += "}\n";
+ }
+
+ if(properties.SEPARATEBACKMAT) {
+ shader += " if(!gl_FrontFacing) {\n";
+ shader += " color.rgb = " + x3dom.shader.decodeGamma(properties, "backDiffuseColor") + ";\n";
+ shader += " color.a = 1.0 - backTransparency;\n";
+ shader += " _shininess = backShininess;\n";
+ shader += " _emissiveColor = backEmissiveColor;\n";
+ shader += " _specularColor = backSpecularColor;\n";
+ shader += " _ambientIntensity = backAmbientIntensity;\n";
+ shader += " }\n";
+ }
+
+
+ //Calculate lights
+ if (properties.LIGHTS) {
+ shader += "vec3 ads;\n";
+
+ for(var l=0; l<properties.LIGHTS; l++) {
+ var lightCol = "light"+l+"_Color";
+ shader += "ads = lighting(light"+l+"_Type, " +
+ "light"+l+"_Location, " +
+ "light"+l+"_Direction, " +
+ lightCol + ", " +
+ "light"+l+"_Attenuation, " +
+ "light"+l+"_Radius, " +
+ "light"+l+"_Intensity, " +
+ "light"+l+"_AmbientIntensity, " +
+ "light"+l+"_BeamWidth, " +
+ "light"+l+"_CutOffAngle, " +
+ "normal, eye, _shininess, _ambientIntensity);\n";
+ shader += " ambient += " + lightCol + " * ads.r;\n" +
+ " diffuse += " + lightCol + " * ads.g;\n" +
+ " specular += " + lightCol + " * ads.b;\n";
+ }
+
+ shader += "ambient = max(ambient, 0.0);\n";
+ shader += "diffuse = max(diffuse, 0.0);\n";
+ shader += "specular = max(specular, 0.0);\n";
+ }
+
+ //Textures
+ if(properties.TEXTURED || properties.DIFFUSEMAP || properties.DIFFPLACEMENTMAP){
+ if(properties.CUBEMAP) {
+ shader += "vec3 viewDir = normalize(fragViewDir);\n";
+ shader += "vec3 reflected = reflect(viewDir, normal);\n";
+ shader += "reflected = (modelViewMatrixInverse * vec4(reflected,0.0)).xyz;\n";
+ shader += "vec4 texColor = " + x3dom.shader.decodeGamma(properties, "textureCube(cubeMap, reflected)") + ";\n";
+ shader += "color.a *= texColor.a;\n";
+ }
+ else if (properties.DIFFPLACEMENTMAP)
+ {
+ shader += "vec2 texCoord = vec2(fragTexcoord.x, 1.0-fragTexcoord.y);\n";
+ shader += "vec4 texColor = texture2D(diffuseDisplacementMap, texCoord);\n";
+ }
+ else
+ {
+ if (properties.PIXELTEX) {
+ shader += "vec2 texCoord = fragTexcoord;\n";
+ } else {
+ shader += "vec2 texCoord = vec2(fragTexcoord.x, 1.0-fragTexcoord.y);\n";
+ }
+ shader += "vec4 texColor = " + x3dom.shader.decodeGamma(properties, "texture2D(diffuseMap, texCoord)") + ";\n";
+ shader += "color.a *= texColor.a;\n";
+ }
+ if(properties.BLENDING){
+ shader += "color.rgb = (_emissiveColor + max(ambient + diffuse, 0.0) * color.rgb + specular*_specularColor);\n";
+ if(properties.CUBEMAP) {
+ shader += "color.rgb = mix(color.rgb, texColor.rgb, vec3(0.75));\n";
+ } else {
+ shader += "color.rgb *= texColor.rgb;\n";
+ }
+ }else{
+ shader += "color.rgb = (_emissiveColor + max(ambient + diffuse, 0.0) * texColor.rgb + specular*_specularColor);\n";
+ }
+ }else{
+ shader += "color.rgb = (_emissiveColor + max(ambient + diffuse, 0.0) * color.rgb + specular*_specularColor);\n";
+ }
+
+ } else {
+ if (properties.APPMAT && !properties.VERTEXCOLOR) {
+ shader += "color = vec4(0.0, 0.0, 0.0, 1.0 - transparency);\n";
+ }
+
+ if(properties.TEXTURED || properties.DIFFUSEMAP){
+ if (properties.PIXELTEX) {
+ shader += "vec2 texCoord = fragTexcoord;\n";
+ } else {
+ shader += "vec2 texCoord = vec2(fragTexcoord.x, 1.0-fragTexcoord.y);\n";
+ }
+
+ if (properties.IS_PARTICLE) {
+ shader += "texCoord = clamp(gl_PointCoord, 0.01, 0.99);\n";
+ }
+ shader += "vec4 texColor = " + x3dom.shader.decodeGamma(properties, "texture2D(diffuseMap, texCoord)") + ";\n";
+ shader += "color.a = texColor.a;\n";
+
+ if(properties.BLENDING || properties.IS_PARTICLE){
+ shader += "color.rgb += _emissiveColor.rgb;\n";
+ shader += "color.rgb *= texColor.rgb;\n";
+ } else {
+ shader += "color = texColor;\n";
+ }
+ } else if(!properties.VERTEXCOLOR && !properties.POINTLINE2D){
+ shader += "color.rgb += _emissiveColor;\n";
+ } else if(!properties.VERTEXCOLOR && properties.POINTLINE2D && !properties.MULTIDIFFALPMAP){
+ shader += "color.rgb = _emissiveColor;\n";
+ if (properties.IS_PARTICLE) {
+ shader += "float pAlpha = 1.0 - clamp(length((gl_PointCoord - 0.5) * 2.0), 0.0, 1.0);\n";
+ shader += "color.rgb *= vec3(pAlpha);\n";
+ shader += "color.a = pAlpha;\n";
+ }
+ } else if (properties.IS_PARTICLE) {
+ shader += "float pAlpha = 1.0 - clamp(length((gl_PointCoord - 0.5) * 2.0), 0.0, 1.0);\n";
+ shader += "color.rgb *= vec3(pAlpha);\n";
+ shader += "color.a = pAlpha;\n";
+ }
+ }
+
+ if(properties.CLIPPLANES)
+ {
+ shader += "if (cappingColor.r != -1.0) {\n";
+ shader += " color.rgb = cappingColor;\n";
+ shader += "}\n";
+ }
+
+ //Kill pixel
+ if(properties.TEXT) {
+ shader += "if (color.a <= 0.5) discard;\n";
+ } else {
+ shader += "if (color.a <= 0.1) discard;\n";
+ }
+
+ //Output the gamma encoded result.
+ shader += "color = clamp(color, 0.0, 1.0);\n";
+ shader += "color = " + x3dom.shader.encodeGamma(properties, "color") + ";\n";
+
+ //Fog
+ if(properties.FOG){
+ shader += "float f0 = calcFog(fragEyePosition);\n";
+ shader += "color.rgb = fogColor * (1.0-f0) + f0 * (color.rgb);\n";
+ }
+
+ shader += "gl_FragColor = color;\n";
+
+ //End Of Shader
+ shader += "}\n";
+
+ var fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
+ gl.shaderSource(fragmentShader, shader);
+ gl.compileShader(fragmentShader);
+
+ if(!gl.getShaderParameter(fragmentShader, gl.COMPILE_STATUS)){
+ x3dom.debug.logInfo("FRAGMENT:\n" + shader);
+ x3dom.debug.logError("FragmentShader " + gl.getShaderInfoLog(fragmentShader));
+ }
+
+ return fragmentShader;
+};
+
+/*
+ * X3DOM JavaScript Library
+ * http://www.x3dom.org
+ *
+ * (C)2009 Fraunhofer IGD, Darmstadt, Germany
+ * Dual licensed under the MIT and GPL
+ *
+ * Based on code originally provided by
+ * Philip Taylor: http://philip.html5.org
+ */
+
+/**
+ * Generate the final Shader program
+ */
+x3dom.shader.DynamicMobileShader = function(gl, properties)
+{
+ this.program = gl.createProgram();
+
+ var vertexShader = this.generateVertexShader(gl, properties);
+ var fragmentShader = this.generateFragmentShader(gl, properties);
+
+ gl.attachShader(this.program, vertexShader);
+ gl.attachShader(this.program, fragmentShader);
+
+ // optional, but position should be at location 0 for performance reasons
+ gl.bindAttribLocation(this.program, 0, "position");
+
+ gl.linkProgram(this.program);
+
+ return this.program;
+};
+
+/**
+ * Generate the vertex shader
+ */
+x3dom.shader.DynamicMobileShader.prototype.generateVertexShader = function(gl, properties)
+{
+ var shader = "";
+
+ /*******************************************************************************
+ * Generate dynamic attributes & uniforms & varyings
+ ********************************************************************************/
+
+ //Material
+ shader += x3dom.shader.material();
+
+ if (properties.TWOSIDEDMAT ) {
+ shader += x3dom.shader.twoSidedMaterial();
+ }
+
+ //Default Matrices
+ shader += "uniform mat4 normalMatrix;\n";
+ shader += "uniform mat4 modelViewMatrix;\n";
+ shader += "uniform mat4 modelViewProjectionMatrix;\n";
+
+ //Positions
+ if(properties.POSCOMPONENTS == 3) {
+ shader += "attribute vec3 position;\n";
+ } else if(properties.POSCOMPONENTS == 4) {
+ shader += "attribute vec4 position;\n";
+ }
+
+ //IG stuff
+ if(properties.IMAGEGEOMETRY) {
+ shader += "uniform vec3 IG_bboxMin;\n";
+ shader += "uniform vec3 IG_bboxMax;\n";
+ shader += "uniform float IG_coordTextureWidth;\n";
+ shader += "uniform float IG_coordTextureHeight;\n";
+ shader += "uniform vec2 IG_implicitMeshSize;\n";
+
+ for( var i = 0; i < properties.IG_PRECISION; i++ ) {
+ shader += "uniform sampler2D IG_coords" + i + "\n;";
+ }
+
+ if(properties.IG_INDEXED) {
+ shader += "uniform sampler2D IG_index;\n";
+ shader += "uniform float IG_indexTextureWidth;\n";
+ shader += "uniform float IG_indexTextureHeight;\n";
+ }
+ }
+
+ //PG stuff
+ if (properties.POPGEOMETRY) {
+ shader += "uniform float PG_precisionLevel;\n";
+ shader += "uniform float PG_powPrecision;\n";
+ shader += "uniform vec3 PG_maxBBSize;\n";
+ shader += "uniform vec3 PG_bbMin;\n";
+ shader += "uniform vec3 PG_bbMaxModF;\n";
+ shader += "uniform vec3 PG_bboxShiftVec;\n";
+ shader += "uniform float PG_numAnchorVertices;\n";
+ shader += "attribute float PG_vertexID;\n";
+ }
+
+ //Normals
+ if(!properties.POINTLINE2D) {
+ if(properties.IMAGEGEOMETRY) {
+ shader += "uniform sampler2D IG_normals;\n";
+ } else {
+ if(properties.NORCOMPONENTS == 2) {
+ if(properties.POSCOMPONENTS != 4) {
+ shader += "attribute vec2 normal;\n";
+ }
+ } else if(properties.NORCOMPONENTS == 3) {
+ shader += "attribute vec3 normal;\n";
+ }
+ }
+ }
+
+ //Colors
+ shader += "varying vec4 fragColor;\n";
+ if(properties.VERTEXCOLOR){
+ if(properties.IMAGEGEOMETRY) {
+ shader += "uniform sampler2D IG_colors;";
+ } else {
+ if(properties.COLCOMPONENTS == 3){
+ shader += "attribute vec3 color;";
+ } else if(properties.COLCOMPONENTS == 4) {
+ shader += "attribute vec4 color;";
+ }
+ }
+ }
+
+ //Textures
+ if(properties.TEXTURED) {
+ shader += "varying vec2 fragTexcoord;\n";
+ if(properties.IMAGEGEOMETRY) {
+ shader += "uniform sampler2D IG_texCoords;";
+ } else {
+ shader += "attribute vec2 texcoord;\n";
+ }
+ if(properties.TEXTRAFO){
+ shader += "uniform mat4 texTrafoMatrix;\n";
+ }
+ if(!properties.BLENDING) {
+ shader += "varying vec3 fragAmbient;\n";
+ shader += "varying vec3 fragDiffuse;\n";
+ }
+ if(properties.CUBEMAP) {
+ shader += "varying vec3 fragViewDir;\n";
+ shader += "varying vec3 fragNormal;\n";
+ shader += "uniform mat4 viewMatrix;\n";
+ }
+ }
+
+ //Fog
+ if(properties.FOG) {
+ shader += x3dom.shader.fog();
+ }
+
+ //Lights
+ if(properties.LIGHTS) {
+ shader += x3dom.shader.light(properties.LIGHTS);
+ }
+
+ //Bounding Boxes
+ if(properties.REQUIREBBOX) {
+ shader += "uniform vec3 bgCenter;\n";
+ shader += "uniform vec3 bgSize;\n";
+ shader += "uniform float bgPrecisionMax;\n";
+ }
+ if(properties.REQUIREBBOXNOR) {
+ shader += "uniform float bgPrecisionNorMax;\n";
+ }
+ if(properties.REQUIREBBOXCOL) {
+ shader += "uniform float bgPrecisionColMax;\n";
+ }
+ if(properties.REQUIREBBOXTEX) {
+ shader += "uniform float bgPrecisionTexMax;\n";
+ }
+
+
+ /*******************************************************************************
+ * Generate main function
+ ********************************************************************************/
+ shader += "void main(void) {\n";
+
+ //Set point size
+ shader += "gl_PointSize = 2.0;\n";
+
+ /*******************************************************************************
+ * Start of ImageGeometry switch
+ ********************************************************************************/
+ if(properties.IMAGEGEOMETRY) {
+ //Indices
+ if(properties.IG_INDEXED) {
+ shader += "vec2 halfPixel = vec2(0.5/IG_indexTextureWidth,0.5/IG_indexTextureHeight);\n";
+ shader += "vec2 IG_texCoord = vec2(position.x*(IG_implicitMeshSize.x/IG_indexTextureWidth), position.y*(IG_implicitMeshSize.y/IG_indexTextureHeight)) + halfPixel;\n";
+ shader += "vec2 IG_indices = texture2D( IG_index, IG_texCoord ).rg;\n";
+ shader += "halfPixel = vec2(0.5/IG_coordTextureWidth,0.5/IG_coordTextureHeight);\n";
+ shader += "IG_texCoord = (IG_indices * 0.996108948) + halfPixel;\n";
+ } else {
+ shader += "vec2 halfPixel = vec2(0.5/IG_coordTextureWidth, 0.5/IG_coordTextureHeight);\n";
+ shader += "vec2 IG_texCoord = vec2(position.x*(IG_implicitMeshSize.x/IG_coordTextureWidth), position.y*(IG_implicitMeshSize.y/IG_coordTextureHeight)) + halfPixel;\n";
+ }
+
+ //Positions
+ shader += "vec3 temp = vec3(0.0, 0.0, 0.0);\n";
+ shader += "vec3 vertPosition = vec3(0.0, 0.0, 0.0);\n";
+
+ for(var i=0; i<properties.IG_PRECISION; i++) {
+ shader += "temp = 255.0 * texture2D( IG_coords" + i + ", IG_texCoord ).rgb;\n";
+ shader += "vertPosition *= 256.0;\n";
+ shader += "vertPosition += temp;\n";
+ }
+
+ shader += "vertPosition /= (pow(2.0, 8.0 * " + properties.IG_PRECISION + ".0) - 1.0);\n";
+ shader += "vertPosition = vertPosition * (IG_bboxMax - IG_bboxMin) + IG_bboxMin;\n";
+
+ //Normals
+ if(!properties.POINTLINE2D) {
+ shader += "vec3 vertNormal = texture2D( IG_normals, IG_texCoord ).rgb;\n";
+ shader += "vertNormal = vertNormal * 2.0 - 1.0;\n";
+ }
+
+ //Colors
+ if(properties.VERTEXCOLOR) {
+ if(properties.COLCOMPONENTS == 3) {
+ shader += "vec3 vertColor = texture2D( IG_colors, IG_texCoord ).rgb;";
+ } else if(properties.COLCOMPONENTS == 4) {
+ shader += "vec4 vertColor = texture2D( IG_colors, IG_texCoord ).rgba;";
+ }
+ }
+
+ //TexCoords
+ if(properties.TEXTURED) {
+ shader += "vec4 IG_doubleTexCoords = texture2D( IG_texCoords, IG_texCoord );\n";
+ shader += "vec2 vertTexCoord;";
+ shader += "vertTexCoord.r = (IG_doubleTexCoords.r * 0.996108948) + (IG_doubleTexCoords.b * 0.003891051);\n";
+ shader += "vertTexCoord.g = (IG_doubleTexCoords.g * 0.996108948) + (IG_doubleTexCoords.a * 0.003891051);\n";
+ }
+ } else {
+ //Positions
+ shader += "vec3 vertPosition = position.xyz;\n";
+
+ if (properties.POPGEOMETRY) {
+ //compute offset using bounding box and test if vertPosition <= PG_bbMaxModF
+ shader += "vec3 offsetVec = step(vertPosition / bgPrecisionMax, PG_bbMaxModF) * PG_bboxShiftVec;\n";
+
+ //coordinate truncation, computation of current maximum possible value
+ //PG_vertexID currently mimics use of gl_VertexID
+ shader += "if ((PG_precisionLevel <= 2.0) || PG_vertexID >= PG_numAnchorVertices) {\n";
+ shader += " vertPosition = floor(vertPosition / PG_powPrecision) * PG_powPrecision;\n";
+ shader += " vertPosition /= (65536.0 - PG_powPrecision);\n";
+ shader += "}\n";
+ shader += "else {\n";
+ shader += " vertPosition /= bgPrecisionMax;\n";
+ shader += "}\n";
+
+ //translate coordinates, where PG_bbMin := floor(bbMin / size)
+ shader += "vertPosition = (vertPosition + offsetVec + PG_bbMin) * PG_maxBBSize;\n";
+ }
+ else if(properties.REQUIREBBOX) {
+ shader += "vertPosition = bgCenter + bgSize * vertPosition / bgPrecisionMax;\n";
+ }
+
+ //Normals
+ if(!properties.POINTLINE2D) {
+ if (properties.NORCOMPONENTS == 2) {
+ if (properties.POSCOMPONENTS == 4) {
+ // (theta, phi) encoded in low/high byte of position.w
+ shader += "vec3 vertNormal = vec3(position.w / 256.0); \n";
+ shader += "vertNormal.x = floor(vertNormal.x) / 255.0; \n";
+ shader += "vertNormal.y = fract(vertNormal.y) * 1.00392156862745; \n"; //256.0 / 255.0
+ } else if (properties.REQUIREBBOXNOR) {
+ shader += "vec3 vertNormal = vec3(normal.xy, 0.0) / bgPrecisionNorMax;\n";
+ } else {
+ shader += "vec3 vertNormal = vec3(normal.xy, 0.0);\n";
+ }
+
+ shader += "vec2 thetaPhi = 3.14159265358979 * vec2(vertNormal.x, vertNormal.y*2.0-1.0); \n";
+
+ // Doing approximation with Taylor series and using cos(x) = sin(x+PI/2)
+ shader += "vec4 sinCosThetaPhi = vec4(thetaPhi, thetaPhi + 1.5707963267949); \n";
+
+ shader += "vec4 thetaPhiPow2 = sinCosThetaPhi * sinCosThetaPhi; \n";
+ shader += "vec4 thetaPhiPow3 = thetaPhiPow2 * sinCosThetaPhi; \n";
+ shader += "vec4 thetaPhiPow5 = thetaPhiPow3 * thetaPhiPow2; \n";
+ shader += "vec4 thetaPhiPow7 = thetaPhiPow5 * thetaPhiPow2; \n";
+ shader += "vec4 thetaPhiPow9 = thetaPhiPow7 * thetaPhiPow2; \n";
+
+ shader += "sinCosThetaPhi += -0.16666666667 * thetaPhiPow3; \n";
+ shader += "sinCosThetaPhi += 0.00833333333 * thetaPhiPow5; \n";
+ shader += "sinCosThetaPhi += -0.000198412698 * thetaPhiPow7; \n";
+ shader += "sinCosThetaPhi += 0.0000027557319 * thetaPhiPow9; \n";
+
+ shader += "vertNormal.x = sinCosThetaPhi.x * sinCosThetaPhi.w; \n";
+ shader += "vertNormal.y = sinCosThetaPhi.x * sinCosThetaPhi.y; \n";
+ shader += "vertNormal.z = sinCosThetaPhi.z; \n";
+ } else {
+ shader += "vec3 vertNormal = normal;\n";
+ if (properties.REQUIREBBOXNOR) {
+ shader += "vertNormal = vertNormal / bgPrecisionNorMax;\n";
+ }
+ if (properties.POPGEOMETRY) {
+ shader += "vertNormal = 2.0*vertNormal - 1.0;\n";
+ }
+ }
+ }
+
+ //Colors
+ if(properties.VERTEXCOLOR) {
+ if(properties.COLCOMPONENTS == 3) {
+ shader += "vec3 vertColor = color;";
+ } else if(properties.COLCOMPONENTS == 4) {
+ shader += "vec4 vertColor = color;";
+ }
+ if(properties.REQUIREBBOXCOL) {
+ shader += "vertColor = vertColor / bgPrecisionColMax;\n";
+ }
+ }
+
+ //TexCoords
+ if(properties.TEXTURED) {
+ shader += "vec2 vertTexCoord = texcoord;\n";
+ if(properties.REQUIREBBOXTEX) {
+ shader += "vertTexCoord = vertTexCoord / bgPrecisionTexMax;\n";
+ }
+ }
+ }
+ /*******************************************************************************
+ * End of ImageGeometry switch
+ ********************************************************************************/
+
+ //positions to model-view-space
+ shader += "vec3 positionMV = (modelViewMatrix * vec4(vertPosition, 1.0)).xyz;\n";
+
+ //normals to model-view-space
+ if(!properties.POINTLINE2D) {
+ shader += "vec3 normalMV = normalize( (normalMatrix * vec4(vertNormal, 0.0)).xyz );\n";
+ }
+
+ shader += "vec3 eye = -positionMV;\n";
+
+ //Colors
+ if (properties.VERTEXCOLOR) {
+ shader += "vec3 rgb = vertColor.rgb;\n";
+ if(properties.COLCOMPONENTS == 4) {
+ shader += "float alpha = vertColor.a;\n";
+ } else if(properties.COLCOMPONENTS == 3) {
+ shader += "float alpha = 1.0 - transparency;\n";
+ }
+ } else {
+ shader += "vec3 rgb = diffuseColor;\n";
+ shader += "float alpha = 1.0 - transparency;\n";
+ }
+
+ //Calc TexCoords
+ if(properties.TEXTURED){
+ if(properties.CUBEMAP) {
+ shader += "fragViewDir = viewMatrix[3].xyz;\n";
+ shader += "fragNormal = normalMV;\n";
+ } else if(properties.SPHEREMAPPING) {
+ shader += " fragTexcoord = 0.5 + normalMV.xy / 2.0;\n";
+ } else if(properties.TEXTRAFO) {
+ shader += " fragTexcoord = (texTrafoMatrix * vec4(vertTexCoord, 1.0, 1.0)).xy;\n";
+ } else {
+ shader += " fragTexcoord = vertTexCoord;\n";
+
+ // LOD LUT HACK ###
+ if (properties.POPGEOMETRY && x3dom.debug.usePrecisionLevelAsTexCoord === true)
+ // remap texCoords to texel middle with w = 16 and tc' := 1 / (2 * w) + tc * (w - 1) / w
+ shader += "fragTexcoord = vec2(0.03125 + 0.9375 * (PG_precisionLevel / 16.0), 1.0);";
+ // LOD LUT HACK ###
+ }
+ }
+
+ //calc lighting
+ if(properties.LIGHTS) {
+ shader += "vec3 ambient = vec3(0.0, 0.0, 0.0);\n";
+ shader += "vec3 diffuse = vec3(0.0, 0.0, 0.0);\n";
+ shader += "vec3 specular = vec3(0.0, 0.0, 0.0);\n";
+ shader += "float _shininess = shininess;\n";
+ shader += "vec3 _specularColor = specularColor;\n";
+ shader += "vec3 _emissiveColor = emissiveColor;\n";
+ shader += "float _ambientIntensity = ambientIntensity;\n";
+
+ //Solid
+ if(!properties.SOLID || properties.TWOSIDEDMAT) {
+ shader += "if (dot(normalMV, eye) < 0.0) {\n";
+ shader += " normalMV *= -1.0;\n";
+ if(properties.SEPARATEBACKMAT) {
+ shader += " rgb = backDiffuseColor;\n";
+ shader += " alpha = 1.0 - backTransparency;\n";
+ shader += " _shininess = backShininess;\n";
+ shader += " _emissiveColor = backEmissiveColor;\n";
+ shader += " _specularColor = backSpecularColor;\n";
+ shader += " _ambientIntensity = backAmbientIntensity;\n";
+ }
+ shader += " }\n";
+ }
+
+ //Calculate lighting
+ if (properties.LIGHTS) {
+ shader += "vec3 ads;\n";
+
+ for(var l=0; l<properties.LIGHTS; l++) {
+ var lightCol = "light"+l+"_Color";
+ shader += "ads = lighting(light"+l+"_Type, " +
+ "light"+l+"_Location, " +
+ "light"+l+"_Direction, " +
+ lightCol + ", " +
+ "light"+l+"_Attenuation, " +
+ "light"+l+"_Radius, " +
+ "light"+l+"_Intensity, " +
+ "light"+l+"_AmbientIntensity, " +
+ "light"+l+"_BeamWidth, " +
+ "light"+l+"_CutOffAngle, " +
+ "normalMV, eye, _shininess, _ambientIntensity);\n";
+ shader += " ambient += " + lightCol + " * ads.r;\n" +
+ " diffuse += " + lightCol + " * ads.g;\n" +
+ " specular += " + lightCol + " * ads.b;\n";
+ }
+
+ shader += "ambient = max(ambient, 0.0);\n";
+ shader += "diffuse = max(diffuse, 0.0);\n";
+ shader += "specular = max(specular, 0.0);\n";
+ }
+
+ //Textures & blending
+ if(properties.TEXTURED && !properties.BLENDING) {
+ shader += "fragAmbient = ambient;\n";
+ shader += "fragDiffuse = diffuse;\n";
+ shader += "fragColor.rgb = (_emissiveColor + specular*_specularColor);\n";
+ shader += "fragColor.a = alpha;\n";
+ } else {
+ shader += "fragColor.rgb = (_emissiveColor + max(ambient + diffuse, 0.0) * rgb + specular*_specularColor);\n";
+ shader += "fragColor.a = alpha;\n";
+ }
+ } else {
+ if (properties.APPMAT && !properties.VERTEXCOLOR) {
+ shader += "rgb = vec3(0.0, 0.0, 0.0);\n";
+ }
+ if(properties.TEXTURED && !properties.BLENDING) {
+ shader += "fragAmbient = vec3(0.0);\n";
+ shader += "fragDiffuse = vec3(1.0);\n";
+ shader += "fragColor.rgb = vec3(0.0);\n";
+ shader += "fragColor.a = alpha;\n";
+ } else if(!properties.VERTEXCOLOR && properties.POINTLINE2D){
+ shader += "fragColor.rgb = emissiveColor;\n";
+ shader += "fragColor.a = alpha;\n";
+ } else {
+ shader += "fragColor.rgb = rgb + emissiveColor;\n";
+ shader += "fragColor.a = alpha;\n";
+ }
+ }
+
+ //Fog
+ if(properties.FOG) {
+ shader += "float f0 = calcFog(-positionMV);\n";
+ shader += "fragColor.rgb = fogColor * (1.0-f0) + f0 * (fragColor.rgb);\n";
+ }
+
+ //Output
+ shader += "gl_Position = modelViewProjectionMatrix * vec4(vertPosition, 1.0);\n";
+
+ //End of shader
+ shader += "}\n";
+
+ var vertexShader = gl.createShader(gl.VERTEX_SHADER);
+ gl.shaderSource(vertexShader, shader);
+ gl.compileShader(vertexShader);
+
+ if(!gl.getShaderParameter(vertexShader, gl.COMPILE_STATUS)){
+ x3dom.debug.logError("[DynamicMobileShader] VertexShader " + gl.getShaderInfoLog(vertexShader));
+ }
+
+ return vertexShader;
+};
+
+/**
+ * Generate the fragment shader
+ */
+x3dom.shader.DynamicMobileShader.prototype.generateFragmentShader = function(gl, properties)
+{
+ var shader = "#ifdef GL_FRAGMENT_PRECISION_HIGH\n";
+ shader += "precision highp float;\n";
+ shader += "#else\n";
+ shader += " precision mediump float;\n";
+ shader += "#endif\n\n";
+
+ /*******************************************************************************
+ * Generate dynamic uniforms & varyings
+ ********************************************************************************/
+ //Colors
+ shader += "varying vec4 fragColor;\n";
+
+ //Textures
+ if(properties.TEXTURED) {
+ if(properties.CUBEMAP) {
+ shader += "uniform samplerCube cubeMap;\n";
+ shader += "varying vec3 fragViewDir;\n";
+ shader += "varying vec3 fragNormal;\n";
+ shader += "uniform mat4 modelViewMatrixInverse;\n";
+ } else {
+ shader += "uniform sampler2D diffuseMap; \n";
+ shader += "varying vec2 fragTexcoord; \n";
+ }
+ if(!properties.BLENDING) {
+ shader += "varying vec3 fragAmbient;\n";
+ shader += "varying vec3 fragDiffuse;\n";
+ }
+ }
+
+ /*******************************************************************************
+ * Generate main function
+ ********************************************************************************/
+ shader += "void main(void) {\n";
+
+ //Colors
+ shader += "vec4 color = fragColor;\n";
+
+ //Textures
+ if(properties.TEXTURED){
+ if(properties.CUBEMAP) {
+ shader += "vec3 normal = normalize(fragNormal);\n";
+ shader += "vec3 viewDir = normalize(fragViewDir);\n";
+ shader += "vec3 reflected = reflect(viewDir, normal);\n";
+ shader += "reflected = (modelViewMatrixInverse * vec4(reflected,0.0)).xyz;\n";
+ shader += "vec4 texColor = textureCube(cubeMap, reflected);\n";
+ } else {
+ shader += "vec4 texColor = texture2D(diffuseMap, vec2(fragTexcoord.s, 1.0-fragTexcoord.t));\n";
+ }
+ if(properties.BLENDING) {
+ if(properties.CUBEMAP) {
+ shader += "color.rgb = mix(color.rgb, texColor.rgb, vec3(0.75));\n";
+ shader += "color.a = texColor.a;\n";
+ } else {
+ shader += "color.rgb *= texColor.rgb;\n";
+ shader += "color.a *= texColor.a;\n";
+ }
+ } else {
+ shader += "color.rgb += max(fragAmbient + fragDiffuse, 0.0) * texColor.rgb;\n";
+ shader += "color.a *= texColor.a;\n";
+ }
+ }
+
+ //Kill pixel
+ if(properties.TEXT) {
+ shader += "if (color.a <= 0.5) discard;\n";
+ } else {
+ shader += "if (color.a <= 0.1) discard;\n";
+ }
+
+ //Output
+ shader += "gl_FragColor = clamp(color, 0.0, 1.0);\n";
+
+ //End of shader
+ shader += "}\n";
+
+ var fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
+ gl.shaderSource(fragmentShader, shader);
+ gl.compileShader(fragmentShader);
+
+ if(!gl.getShaderParameter(fragmentShader, gl.COMPILE_STATUS)){
+ x3dom.debug.logError("[DynamicMobileShader] FragmentShader " + gl.getShaderInfoLog(fragmentShader));
+ }
+
+ return fragmentShader;
+};
+
+/*
+ * X3DOM JavaScript Library
+ * http://www.x3dom.org
+ *
+ * (C)2009 Fraunhofer IGD, Darmstadt, Germany
+ * Dual licensed under the MIT and GPL
+ *
+ * Based on code originally provided by
+ * Philip Taylor: http://philip.html5.org
+ */
+
+/**
+ * Generate the final Shader program
+ */
+x3dom.shader.DynamicShaderPicking = function(gl, properties, pickMode)
+{
+ this.program = gl.createProgram();
+
+ var vertexShader = this.generateVertexShader(gl, properties, pickMode);
+ var fragmentShader = this.generateFragmentShader(gl, properties, pickMode);
+
+ gl.attachShader(this.program, vertexShader);
+ gl.attachShader(this.program, fragmentShader);
+
+ // optional, but position should be at location 0 for performance reasons
+ gl.bindAttribLocation(this.program, 0, "position");
+
+ gl.linkProgram(this.program);
+
+ return this.program;
+};
+
+/**
+ * Generate the vertex shader
+ */
+x3dom.shader.DynamicShaderPicking.prototype.generateVertexShader = function(gl, properties, pickMode)
+{
+ var shader = "";
+
+ /*******************************************************************************
+ * Generate dynamic attributes & uniforms & varyings
+ ********************************************************************************/
+
+ //Default Matrices
+ shader += "uniform mat4 modelMatrix;\n";
+ shader += "uniform mat4 modelViewProjectionMatrix;\n";
+
+ shader += "attribute vec3 position;\n";
+ shader += "uniform vec3 from;\n";
+ shader += "varying vec3 worldCoord;\n";
+
+ if(pickMode == 1) { // Color Picking
+ shader += "attribute vec3 color;\n";
+ shader += "varying vec3 fragColor;\n";
+ } else if(pickMode == 2){ //TexCoord Picking
+ shader += "attribute vec2 texcoord;\n";
+ shader += "varying vec3 fragColor;\n";
+ }
+
+ //Bounding stuff
+ if(properties.REQUIREBBOX) {
+ shader += "uniform vec3 bgCenter;\n";
+ shader += "uniform vec3 bgSize;\n";
+ shader += "uniform float bgPrecisionMax;\n";
+ }
+ if(properties.REQUIREBBOXCOL) {
+ shader += "uniform float bgPrecisionColMax;\n";
+ }
+ if(properties.REQUIREBBOXTEX) {
+ shader += "uniform float bgPrecisionTexMax;\n";
+ }
+
+ //ShadowID stuff
+ if(properties.VERTEXID) {
+ shader += "uniform float shadowIDs;\n";
+
+ if (pickMode == 3) { //24bit
+ shader += "varying vec3 idCoord;\n";
+ } else {
+ shader += "varying vec2 idCoord;\n";
+ }
+ shader += "varying float fragID;\n";
+ shader += "attribute float id;\n";
+ }
+
+ //ImageGeometry stuff
+ if(properties.IMAGEGEOMETRY) {
+ shader += "uniform vec3 IG_bboxMin;\n";
+ shader += "uniform vec3 IG_bboxMax;\n";
+ shader += "uniform float IG_coordTextureWidth;\n";
+ shader += "uniform float IG_coordTextureHeight;\n";
+ shader += "uniform vec2 IG_implicitMeshSize;\n";
+
+ for( var i = 0; i < properties.IG_PRECISION; i++ ) {
+ shader += "uniform sampler2D IG_coords" + i + "\n;";
+ }
+
+ if(properties.IG_INDEXED) {
+ shader += "uniform sampler2D IG_index;\n";
+ shader += "uniform float IG_indexTextureWidth;\n";
+ shader += "uniform float IG_indexTextureHeight;\n";
+ }
+ }
+
+ //PopGeometry
+ if (properties.POPGEOMETRY) {
+ shader += "uniform float PG_precisionLevel;\n";
+ shader += "uniform float PG_powPrecision;\n";
+ shader += "uniform vec3 PG_maxBBSize;\n";
+ shader += "uniform vec3 PG_bbMin;\n";
+ shader += "uniform vec3 PG_bbMaxModF;\n";
+ shader += "uniform vec3 PG_bboxShiftVec;\n";
+ shader += "uniform float PG_numAnchorVertices;\n";
+ shader += "attribute float PG_vertexID;\n";
+ }
+
+ //ClipPlane stuff
+ if(properties.CLIPPLANES) {
+ shader += "uniform mat4 modelViewMatrix;\n";
+ shader += "varying vec4 fragPosition;\n";
+ }
+
+
+ /*******************************************************************************
+ * Generate main function
+ ********************************************************************************/
+
+ shader += "void main(void) {\n";
+
+ shader += "gl_PointSize = 2.0;\n";
+ shader += "vec3 pos = position;\n";
+
+
+ if(properties.VERTEXID) {
+ if(pickMode == 0) { //Default Picking
+ shader += "idCoord = vec2((id + shadowIDs) / 256.0);\n";
+ shader += "idCoord.x = floor(idCoord.x) / 255.0;\n";
+ shader += "idCoord.y = fract(idCoord.y) * 1.00392156862745;\n";
+ shader += "fragID = id;\n";
+ } else if(pickMode == 3) { //Picking with 24bit precision
+ //composed id is at least 32 (= 2*16) bit + num bits for max-orig-shape-id
+ shader += "float ID = id + shadowIDs;\n";
+ //however, let's ignore this and assume a maximum of 24 bits for all id's
+ shader += "float h = floor(ID / 256.0);\n";
+ shader += "idCoord.x = ID - (h * 256.0);\n";
+ shader += "idCoord.z = floor(h / 256.0);\n";
+ shader += "idCoord.y = h - (idCoord.z * 256.0);\n";
+ shader += "idCoord = idCoord.zyx / 255.0;\n";
+ shader += "fragID = id;\n";
+ } else if(pickMode == 4) { //Picking with 32bit precision
+ shader += "idCoord = vec2((id + shadowIDs) / 256.0);\n";
+ shader += "idCoord.x = floor(idCoord.x) / 255.0;\n";
+ shader += "idCoord.y = fract(idCoord.y) * 1.00392156862745;\n";
+ shader += "fragID = id;\n";
+ }
+ }
+
+ /*******************************************************************************
+ * Start of special Geometry switch
+ ********************************************************************************/
+ if(properties.IMAGEGEOMETRY) { //ImageGeometry
+ if(properties.IG_INDEXED) {
+ shader += "vec2 halfPixel = vec2(0.5/IG_indexTextureWidth,0.5/IG_indexTextureHeight);\n";
+ shader += "vec2 IG_texCoord = vec2(position.x*(IG_implicitMeshSize.x/IG_indexTextureWidth), position.y*(IG_implicitMeshSize.y/IG_indexTextureHeight)) + halfPixel;\n";
+ shader += "vec2 IG_indices = texture2D( IG_index, IG_texCoord ).rg;\n";
+ shader += "halfPixel = vec2(0.5/IG_coordTextureWidth,0.5/IG_coordTextureHeight);\n";
+ shader += "IG_texCoord = (IG_indices * 0.996108948) + halfPixel;\n";
+ } else {
+ shader += "vec2 halfPixel = vec2(0.5/IG_coordTextureWidth, 0.5/IG_coordTextureHeight);\n";
+ shader += "vec2 IG_texCoord = vec2(position.x*(IG_implicitMeshSize.x/IG_coordTextureWidth), position.y*(IG_implicitMeshSize.y/IG_coordTextureHeight)) + halfPixel;\n";
+ }
+
+ shader += "pos = texture2D( IG_coordinateTexture, IG_texCoord ).rgb;\n";
+ shader += "pos = pos * (IG_bboxMax - IG_bboxMin) + IG_bboxMin;\n";
+ } else if (properties.POPGEOMETRY) { //PopGeometry
+ shader += "vec3 offsetVec = step(pos / bgPrecisionMax, PG_bbMaxModF) * PG_bboxShiftVec;\n";
+ shader += "if (PG_precisionLevel <= 2.0) {\n";
+ shader += "pos = floor(pos / PG_powPrecision) * PG_powPrecision;\n";
+ shader += "pos /= (65536.0 - PG_powPrecision);\n";
+ shader += "}\n";
+ shader += "else {\n";
+ shader += "pos /= bgPrecisionMax;\n";
+ shader += "}\n";
+ shader += "pos = (pos + offsetVec + PG_bbMin) * PG_maxBBSize;\n";
+ } else {
+
+ if(properties.REQUIREBBOX) {
+ shader += "pos = bgCenter + bgSize * pos / bgPrecisionMax;\n";
+ }
+
+ if(pickMode == 1 && !properties.REQUIREBBOXCOL) { //Color Picking
+ shader += "fragColor = color;\n";
+ } else if(pickMode == 1 && properties.REQUIREBBOXCOL) { //Color Picking
+ shader += "fragColor = color / bgPrecisionColMax;\n";
+ } else if(pickMode == 2 && !properties.REQUIREBBOXTEX) { //TexCoord Picking
+ shader += "fragColor = vec3(abs(texcoord.x), abs(texcoord.y), 0.0);\n";
+ } else if(pickMode == 2 && properties.REQUIREBBOXTEX) { //TexCoord Picking
+ shader += "vec2 texCoord = texcoord / bgPrecisionTexMax;\n";
+ shader += "fragColor = vec3(abs(texCoord.x), abs(texCoord.y), 0.0);\n";
+ }
+ }
+
+ if(properties.CLIPPLANES) {
+ shader += "fragPosition = (modelViewMatrix * vec4(pos, 1.0));\n";
+ }
+
+ shader += "worldCoord = (modelMatrix * vec4(pos, 1.0)).xyz - from;\n";
+ shader += "gl_Position = modelViewProjectionMatrix * vec4(pos, 1.0);\n";
+
+
+ //END OF SHADER
+ shader += "}\n";
+
+ var vertexShader = gl.createShader(gl.VERTEX_SHADER);
+ gl.shaderSource(vertexShader, shader);
+ gl.compileShader(vertexShader);
+
+ if(!gl.getShaderParameter(vertexShader, gl.COMPILE_STATUS)){
+ x3dom.debug.logInfo("VERTEX:\n" + shader);
+ x3dom.debug.logError("VertexShader " + gl.getShaderInfoLog(vertexShader));
+ }
+
+ return vertexShader;
+};
+
+/**
+ * Generate the fragment shader
+ */
+x3dom.shader.DynamicShaderPicking.prototype.generateFragmentShader = function(gl, properties, pickMode)
+{
+ var shader = "#ifdef GL_FRAGMENT_PRECISION_HIGH\n";
+ shader += " precision highp float;\n";
+ shader += "#else\n";
+ shader += " precision mediump float;\n";
+ shader += "#endif\n\n";
+
+ /*******************************************************************************
+ * Generate dynamic uniforms & varyings
+ ********************************************************************************/
+
+ shader += "uniform float highBit;\n";
+ shader += "uniform float lowBit;\n";
+ shader += "uniform float sceneSize;\n";
+ shader += "varying vec3 worldCoord;\n";
+
+ if(pickMode == 1 || pickMode == 2) {
+ shader += "varying vec3 fragColor;\n";
+ }
+
+ //ShadowID stuff
+ if(properties.VERTEXID) {
+ if (pickMode == 3) { //24bit
+ shader += "varying vec3 idCoord;\n";
+ } else {
+ shader += "varying vec2 idCoord;\n";
+ }
+ shader += "varying float fragID;\n";
+ }
+
+ //ClipPlane stuff
+ if(properties.CLIPPLANES) {
+ shader += "uniform mat4 viewMatrixInverse;\n";
+ shader += "varying vec4 fragPosition;\n";
+ }
+
+ if (properties.MULTIVISMAP) {
+ shader += "uniform sampler2D multiVisibilityMap;\n";
+ shader += "uniform float multiVisibilityWidth;\n";
+ shader += "uniform float multiVisibilityHeight;\n";
+ }
+
+ if(properties.CLIPPLANES) {
+ shader += x3dom.shader.clipPlanes(properties.CLIPPLANES);
+ }
+
+ /*******************************************************************************
+ * Generate main function
+ ********************************************************************************/
+ shader += "void main(void) {\n";
+
+ if(properties.CLIPPLANES)
+ {
+ shader += "calculateClipPlanes();\n";
+ }
+
+ if(pickMode == 1 || pickMode == 2) { //Picking Color || Picking TexCoord
+ shader += "vec4 color = vec4(fragColor, lowBit);\n";
+ } else if(pickMode == 4) { //Picking with 32bit precision
+ shader += "vec4 color = vec4(highBit, lowBit, 0.0, 0.0);\n";
+ } else {
+ shader += "vec4 color = vec4(0.0, 0.0, highBit, lowBit);\n";
+ }
+
+ if(properties.VERTEXID) {
+ if(pickMode == 0 || pickMode == 4) { //Default Picking || Picking with 32bit precision
+ shader += "color.ba = idCoord;\n";
+ } else if(pickMode == 3) { //Picking with 24bit precision
+ shader += "color.gba = idCoord;\n";
+ }
+
+ if (properties.MULTIVISMAP) {
+ shader += "vec2 idTexCoord;\n";
+ shader += "float roundedID = floor(fragID+0.5);\n";
+ shader += "idTexCoord.x = (mod(roundedID, multiVisibilityWidth)) * (1.0 / multiVisibilityWidth) + (0.5 / multiVisibilityWidth);\n";
+ shader += "idTexCoord.y = (floor(roundedID / multiVisibilityHeight)) * (1.0 / multiVisibilityHeight) + (0.5 / multiVisibilityHeight);\n";
+ shader += "vec4 visibility = texture2D( multiVisibilityMap, idTexCoord );\n";
+ shader += "if (visibility.r < 1.0) discard; \n";
+ }
+ }
+
+ if(pickMode != 1 && pickMode != 2) {
+ shader += "float d = length(worldCoord) / sceneSize;\n";
+ }
+
+ if(pickMode == 0) { //Default Picking
+ shader += "vec2 comp = fract(d * vec2(256.0, 1.0));\n";
+ shader += "color.rg = comp - (comp.rr * vec2(0.0, 1.0/256.0));\n";
+ } else if(pickMode == 3) { //Picking with 24bit precision
+ shader += "color.r = d;\n";
+ }
+
+ shader += "gl_FragColor = color;\n";
+
+ //END OF SHADER
+ shader += "}\n";
+
+ var fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
+ gl.shaderSource(fragmentShader, shader);
+ gl.compileShader(fragmentShader);
+
+ if(!gl.getShaderParameter(fragmentShader, gl.COMPILE_STATUS)){
+ x3dom.debug.logInfo("FRAGMENT:\n" + shader);
+ x3dom.debug.logError("FragmentShader " + gl.getShaderInfoLog(fragmentShader));
+ }
+
+ return fragmentShader;
+};
+
+/*
+ * X3DOM JavaScript Library
+ * http://www.x3dom.org
+ *
+ * (C)2009 Fraunhofer IGD, Darmstadt, Germany
+ * Dual licensed under the MIT and GPL
+ *
+ * Based on code originally provided by
+ * Philip Taylor: http://philip.html5.org
+ */
+
+/**
+ * Generate the final Shader program
+ */
+x3dom.shader.ComposedShader = function(gl, shape)
+{
+ this.program = gl.createProgram();
+
+ var vertexShader = this.generateVertexShader(gl, shape);
+ var fragmentShader = this.generateFragmentShader(gl, shape);
+
+ gl.attachShader(this.program, vertexShader);
+ gl.attachShader(this.program, fragmentShader);
+
+ // optional, but position should be at location 0 for performance reasons
+ gl.bindAttribLocation(this.program, 0, "position");
+
+ gl.linkProgram(this.program);
+
+ return this.program;
+};
+
+/**
+ * Generate the vertex shader
+ */
+x3dom.shader.ComposedShader.prototype.generateVertexShader = function(gl, shape)
+{
+ var shader = shape._cf.appearance.node._shader._vertex._vf.url[0];
+
+ var vertexShader = gl.createShader(gl.VERTEX_SHADER);
+ gl.shaderSource(vertexShader, shader);
+ gl.compileShader(vertexShader);
+
+ if(!gl.getShaderParameter(vertexShader, gl.COMPILE_STATUS)){
+ x3dom.debug.logError("[ComposedShader] VertexShader " + gl.getShaderInfoLog(vertexShader));
+ }
+
+ return vertexShader;
+};
+
+/**
+ * Generate the fragment shader
+ */
+x3dom.shader.ComposedShader.prototype.generateFragmentShader = function(gl, shape)
+{
+ var shader = shape._cf.appearance.node._shader._fragment._vf.url[0];
+
+ var fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
+ gl.shaderSource(fragmentShader, shader);
+ gl.compileShader(fragmentShader);
+
+ if(!gl.getShaderParameter(fragmentShader, gl.COMPILE_STATUS)){
+ x3dom.debug.logError("[ComposedShader] FragmentShader " + gl.getShaderInfoLog(fragmentShader));
+ }
+
+ return fragmentShader;
+};
+
+/*
+ * X3DOM JavaScript Library
+ * http://www.x3dom.org
+ *
+ * (C)2009 Fraunhofer IGD, Darmstadt, Germany
+ * Dual licensed under the MIT and GPL
+ *
+ * Based on code originally provided by
+ * Philip Taylor: http://philip.html5.org
+ */
+
+/**
+ * Generate the final Shader program
+ */
+x3dom.shader.NormalShader = function(gl)
+{
+ this.program = gl.createProgram();
+
+ var vertexShader = this.generateVertexShader(gl);
+ var fragmentShader = this.generateFragmentShader(gl);
+
+ gl.attachShader(this.program, vertexShader);
+ gl.attachShader(this.program, fragmentShader);
+
+ // optional, but position should be at location 0 for performance reasons
+ gl.bindAttribLocation(this.program, 0, "position");
+
+ gl.linkProgram(this.program);
+
+ return this.program;
+};
+
+/**
+ * Generate the vertex shader
+ */
+x3dom.shader.NormalShader.prototype.generateVertexShader = function(gl)
+{
+ var shader = "attribute vec3 position;\n" +
+ "attribute vec3 normal;\n" +
+ "uniform vec3 bgCenter;\n" +
+ "uniform vec3 bgSize;\n" +
+ "uniform float bgPrecisionMax;\n" +
+ "uniform float bgPrecisionNorMax;\n" +
+ "uniform mat4 normalMatrix;\n" +
+ "uniform mat4 modelViewProjectionMatrix;\n" +
+ "varying vec3 fragNormal;\n" +
+
+ "void main(void) {\n" +
+ " vec3 pos = bgCenter + bgSize * position / bgPrecisionMax;\n" +
+ " fragNormal = (normalMatrix * vec4(normal / bgPrecisionNorMax, 0.0)).xyz;\n" +
+ //" fragNormal = normal;\n" +
+ " gl_Position = modelViewProjectionMatrix * vec4(pos, 1.0);\n" +
+ "}\n";
+
+ var vertexShader = gl.createShader(gl.VERTEX_SHADER);
+ gl.shaderSource(vertexShader, shader);
+ gl.compileShader(vertexShader);
+
+ if(!gl.getShaderParameter(vertexShader, gl.COMPILE_STATUS)){
+ x3dom.debug.logError("[NormalShader] VertexShader " + gl.getShaderInfoLog(vertexShader));
+ }
+
+ return vertexShader;
+};
+
+/**
+ * Generate the fragment shader
+ */
+x3dom.shader.NormalShader.prototype.generateFragmentShader = function(gl)
+{
+ var shader = "#ifdef GL_FRAGMENT_PRECISION_HIGH\n";
+ shader += "precision highp float;\n";
+ shader += "#else\n";
+ shader += " precision mediump float;\n";
+ shader += "#endif\n\n";
+
+ shader += "varying vec3 fragNormal;\n" +
+
+ "void main(void) {\n" +
+ " gl_FragColor = vec4(normalize(fragNormal) / 2.0 + 0.5, 1.0);\n" +
+ "}\n";
+
+ var fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
+ gl.shaderSource(fragmentShader, shader);
+ gl.compileShader(fragmentShader);
+
+ if(!gl.getShaderParameter(fragmentShader, gl.COMPILE_STATUS)){
+ x3dom.debug.logError("[NormalShader] FragmentShader " + gl.getShaderInfoLog(fragmentShader));
+ }
+
+ return fragmentShader;
+};
+
+/*
+ * X3DOM JavaScript Library
+ * http://www.x3dom.org
+ *
+ * (C)2009 Fraunhofer IGD, Darmstadt, Germany
+ * Dual licensed under the MIT and GPL
+ *
+ * Based on code originally provided by
+ * Philip Taylor: http://philip.html5.org
+ */
+
+/**
+ * Generate the final ShadowShader program
+ */
+x3dom.shader.FrontgroundTextureShader = function(gl)
+{
+ this.program = gl.createProgram();
+
+ var vertexShader = this.generateVertexShader(gl);
+ var fragmentShader = this.generateFragmentShader(gl);
+
+ gl.attachShader(this.program, vertexShader);
+ gl.attachShader(this.program, fragmentShader);
+
+ // optional, but position should be at location 0 for performance reasons
+ gl.bindAttribLocation(this.program, 0, "position");
+
+ gl.linkProgram(this.program);
+
+ return this.program;
+};
+
+/**
+ * Generate the vertex shader
+ */
+x3dom.shader.FrontgroundTextureShader.prototype.generateVertexShader = function(gl)
+{
+ var shader = "attribute vec3 position;\n" +
+ "varying vec2 fragTexCoord;\n" +
+ "\n" +
+ "void main(void) {\n" +
+ " vec2 texCoord = (position.xy + 1.0) * 0.5;\n" +
+ " fragTexCoord = texCoord;\n" +
+ " gl_Position = vec4(position.xy, 0.0, 1.0);\n" +
+ "}\n";
+
+ var vertexShader = gl.createShader(gl.VERTEX_SHADER);
+ gl.shaderSource(vertexShader, shader);
+ gl.compileShader(vertexShader);
+
+ if(!gl.getShaderParameter(vertexShader, gl.COMPILE_STATUS)){
+ x3dom.debug.logError("[FrontgroundTextureShader] VertexShader " + gl.getShaderInfoLog(vertexShader));
+ }
+
+ return vertexShader;
+};
+
+/**
+ * Generate the fragment shader
+ */
+x3dom.shader.FrontgroundTextureShader.prototype.generateFragmentShader = function(gl)
+{
+ var shader = "#ifdef GL_FRAGMENT_PRECISION_HIGH\n";
+ shader += "precision highp float;\n";
+ shader += "#else\n";
+ shader += " precision mediump float;\n";
+ shader += "#endif\n\n";
+
+ shader += "uniform sampler2D tex;\n" +
+ "varying vec2 fragTexCoord;\n" +
+ "\n" +
+ "void main(void) {\n" +
+ " vec4 col = texture2D(tex, fragTexCoord);\n" +
+ " gl_FragColor = vec4(col.rgb, 1.0);\n" +
+ "}\n";
+
+ var fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
+ gl.shaderSource(fragmentShader, shader);
+ gl.compileShader(fragmentShader);
+
+ if(!gl.getShaderParameter(fragmentShader, gl.COMPILE_STATUS)){
+ x3dom.debug.logError("[FrontgroundTextureShader] FragmentShader " + gl.getShaderInfoLog(fragmentShader));
+ }
+
+ return fragmentShader;
+};
+
+
+/*
+ * X3DOM JavaScript Library
+ * http://www.x3dom.org
+ *
+ * (C)2009 Fraunhofer IGD, Darmstadt, Germany
+ * Dual licensed under the MIT and GPL
+ *
+ * Based on code originally provided by
+ * Philip Taylor: http://philip.html5.org
+ */
+
+/**
+ * Generate the final ShadowShader program
+ */
+x3dom.shader.BackgroundTextureShader = function(gl)
+{
+ this.program = gl.createProgram();
+
+ var vertexShader = this.generateVertexShader(gl);
+ var fragmentShader = this.generateFragmentShader(gl);
+
+ gl.attachShader(this.program, vertexShader);
+ gl.attachShader(this.program, fragmentShader);
+
+ // optional, but position should be at location 0 for performance reasons
+ gl.bindAttribLocation(this.program, 0, "position");
+
+ gl.linkProgram(this.program);
+
+ return this.program;
+};
+
+/**
+ * Generate the vertex shader
+ */
+x3dom.shader.BackgroundTextureShader.prototype.generateVertexShader = function(gl)
+{
+ var shader = "attribute vec3 position;\n" +
+ "varying vec2 fragTexCoord;\n" +
+ "\n" +
+ "void main(void) {\n" +
+ " vec2 texCoord = (position.xy + 1.0) * 0.5;\n" +
+ " fragTexCoord = texCoord;\n" +
+ " gl_Position = vec4(position.xy, 0.0, 1.0);\n" +
+ "}\n";
+
+ var vertexShader = gl.createShader(gl.VERTEX_SHADER);
+ gl.shaderSource(vertexShader, shader);
+ gl.compileShader(vertexShader);
+
+ if(!gl.getShaderParameter(vertexShader, gl.COMPILE_STATUS)){
+ x3dom.debug.logError("[BackgroundTextureShader] VertexShader " + gl.getShaderInfoLog(vertexShader));
+ }
+
+ return vertexShader;
+};
+
+/**
+ * Generate the fragment shader
+ */
+x3dom.shader.BackgroundTextureShader.prototype.generateFragmentShader = function(gl)
+{
+ var shader = "#ifdef GL_FRAGMENT_PRECISION_HIGH\n";
+ shader += "precision highp float;\n";
+ shader += "#else\n";
+ shader += " precision mediump float;\n";
+ shader += "#endif\n\n";
+
+ shader += "uniform sampler2D tex;\n" +
+ "varying vec2 fragTexCoord;\n" +
+ "\n" +
+ "void main(void) {\n" +
+ " gl_FragColor = texture2D(tex, fragTexCoord);\n" +
+ "}";
+
+ var fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
+ gl.shaderSource(fragmentShader, shader);
+ gl.compileShader(fragmentShader);
+
+ if(!gl.getShaderParameter(fragmentShader, gl.COMPILE_STATUS)){
+ x3dom.debug.logError("[BackgroundTextureShader] FragmentShader " + gl.getShaderInfoLog(fragmentShader));
+ }
+
+ return fragmentShader;
+};
+
+
+/*
+ * X3DOM JavaScript Library
+ * http://www.x3dom.org
+ *
+ * (C)2009 Fraunhofer IGD, Darmstadt, Germany
+ * Dual licensed under the MIT and GPL
+ *
+ * Based on code originally provided by
+ * Philip Taylor: http://philip.html5.org
+ */
+
+/**
+ * Generate the final ShadowShader program
+ */
+x3dom.shader.BackgroundSkyTextureShader = function(gl)
+{
+ this.program = gl.createProgram();
+
+ var vertexShader = this.generateVertexShader(gl);
+ var fragmentShader = this.generateFragmentShader(gl);
+
+ gl.attachShader(this.program, vertexShader);
+ gl.attachShader(this.program, fragmentShader);
+
+ // optional, but position should be at location 0 for performance reasons
+ gl.bindAttribLocation(this.program, 0, "position");
+
+ gl.linkProgram(this.program);
+
+ return this.program;
+};
+
+/**
+ * Generate the vertex shader
+ */
+x3dom.shader.BackgroundSkyTextureShader.prototype.generateVertexShader = function(gl)
+{
+ var shader = "attribute vec3 position;\n" +
+ "attribute vec2 texcoord;\n" +
+ "uniform mat4 modelViewProjectionMatrix;\n" +
+ "varying vec2 fragTexCoord;\n" +
+ "\n" +
+ "void main(void) {\n" +
+ " fragTexCoord = texcoord;\n" +
+ " gl_Position = modelViewProjectionMatrix * vec4(position, 1.0);\n" +
+ "}\n";
+
+ var vertexShader = gl.createShader(gl.VERTEX_SHADER);
+ gl.shaderSource(vertexShader, shader);
+ gl.compileShader(vertexShader);
+
+ if(!gl.getShaderParameter(vertexShader, gl.COMPILE_STATUS)){
+ x3dom.debug.logError("[BackgroundSkyTextureShader] VertexShader " + gl.getShaderInfoLog(vertexShader));
+ }
+
+ return vertexShader;
+};
+
+/**
+ * Generate the fragment shader
+ */
+x3dom.shader.BackgroundSkyTextureShader.prototype.generateFragmentShader = function(gl)
+{
+ var shader = "#ifdef GL_FRAGMENT_PRECISION_HIGH\n";
+ shader += "precision highp float;\n";
+ shader += "#else\n";
+ shader += " precision mediump float;\n";
+ shader += "#endif\n\n";
+
+ shader += "uniform sampler2D tex;\n" +
+ "varying vec2 fragTexCoord;\n" +
+ "\n" +
+ "void main(void) {\n" +
+ " gl_FragColor = texture2D(tex, fragTexCoord);\n" +
+ "}\n";
+
+ var fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
+ gl.shaderSource(fragmentShader, shader);
+ gl.compileShader(fragmentShader);
+
+ if(!gl.getShaderParameter(fragmentShader, gl.COMPILE_STATUS)){
+ x3dom.debug.logError("[BackgroundSkyTextureShader] FragmentShader " + gl.getShaderInfoLog(fragmentShader));
+ }
+
+ return fragmentShader;
+};
+
+/*
+ * X3DOM JavaScript Library
+ * http://www.x3dom.org
+ *
+ * (C)2009 Fraunhofer IGD, Darmstadt, Germany
+ * Dual licensed under the MIT and GPL
+ *
+ * Based on code originally provided by
+ * Philip Taylor: http://philip.html5.org
+ */
+
+/**
+ * Generate the final ShadowShader program
+ */
+x3dom.shader.BackgroundCubeTextureShader = function(gl)
+{
+ this.program = gl.createProgram();
+
+ var vertexShader = this.generateVertexShader(gl);
+ var fragmentShader = this.generateFragmentShader(gl);
+
+ gl.attachShader(this.program, vertexShader);
+ gl.attachShader(this.program, fragmentShader);
+
+ // optional, but position should be at location 0 for performance reasons
+ gl.bindAttribLocation(this.program, 0, "position");
+
+ gl.linkProgram(this.program);
+
+ return this.program;
+};
+
+/**
+ * Generate the vertex shader
+ */
+x3dom.shader.BackgroundCubeTextureShader.prototype.generateVertexShader = function(gl)
+{
+ var shader = "attribute vec3 position;\n" +
+ "uniform mat4 modelViewProjectionMatrix;\n" +
+ "varying vec3 fragNormal;\n" +
+ "\n" +
+ "void main(void) {\n" +
+ " fragNormal = normalize(position);\n" +
+ " gl_Position = modelViewProjectionMatrix * vec4(position, 1.0);\n" +
+ "}\n";
+
+ var vertexShader = gl.createShader(gl.VERTEX_SHADER);
+ gl.shaderSource(vertexShader, shader);
+ gl.compileShader(vertexShader);
+
+ if(!gl.getShaderParameter(vertexShader, gl.COMPILE_STATUS)){
+ x3dom.debug.logError("[BackgroundCubeTextureShader] VertexShader " + gl.getShaderInfoLog(vertexShader));
+ }
+
+ return vertexShader;
+};
+
+/**
+ * Generate the fragment shader
+ */
+x3dom.shader.BackgroundCubeTextureShader.prototype.generateFragmentShader = function(gl)
+{
+ var shader = "#ifdef GL_FRAGMENT_PRECISION_HIGH\n";
+ shader += "precision highp float;\n";
+ shader += "#else\n";
+ shader += " precision mediump float;\n";
+ shader += "#endif\n\n";
+
+ shader += "uniform samplerCube tex;\n" +
+ "varying vec3 fragNormal;\n" +
+ "\n" +
+ "float magn(float val) {\n" +
+ " return ((val >= 0.0) ? val : -1.0 * val);\n" +
+ "}" +
+ "\n" +
+ "void main(void) {\n" +
+ " vec3 normal = -reflect(normalize(fragNormal), vec3(0.0,0.0,1.0));\n" +
+ " if (magn(normal.y) >= magn(normal.x) && magn(normal.y) >= magn(normal.z))\n" +
+ " normal.xz = -normal.xz;\n" +
+ " gl_FragColor = textureCube(tex, normal);\n" +
+ "}\n";
+
+ var fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
+ gl.shaderSource(fragmentShader, shader);
+ gl.compileShader(fragmentShader);
+
+ if(!gl.getShaderParameter(fragmentShader, gl.COMPILE_STATUS)){
+ x3dom.debug.logError("[BackgroundCubeTextureShader] FragmentShader " + gl.getShaderInfoLog(fragmentShader));
+ }
+
+ return fragmentShader;
+};
+
+/*
+ * X3DOM JavaScript Library
+ * http://www.x3dom.org
+ *
+ * (C)2009 Fraunhofer IGD, Darmstadt, Germany
+ * Dual licensed under the MIT and GPL
+ *
+ * Based on code originally provided by
+ * Philip Taylor: http://philip.html5.org
+ */
+
+/**
+ * Generate the final ShadowShader program
+ */
+x3dom.shader.ShadowShader = function(gl)
+{
+ this.program = gl.createProgram();
+
+ var vertexShader = this.generateVertexShader(gl);
+ var fragmentShader = this.generateFragmentShader(gl);
+
+ gl.attachShader(this.program, vertexShader);
+ gl.attachShader(this.program, fragmentShader);
+
+ // optional, but position should be at location 0 for performance reasons
+ gl.bindAttribLocation(this.program, 0, "position");
+
+ gl.linkProgram(this.program);
+
+ return this.program;
+};
+
+/**
+ * Generate the vertex shader
+ */
+x3dom.shader.ShadowShader.prototype.generateVertexShader = function(gl)
+{
+ var shader = "";
+ if (!x3dom.caps.MOBILE) {
+
+ shader += "attribute vec3 position;\n" +
+ "uniform mat4 modelViewProjectionMatrix;\n" +
+ "varying vec4 projCoords;\n" +
+ //image geometry
+ "uniform float imageGeometry;\n" +
+ "uniform vec3 IG_bboxMin;\n" +
+ "uniform vec3 IG_bboxMax;\n" +
+ "uniform float IG_coordTextureWidth;\n" +
+ "uniform float IG_coordTextureHeight;\n" +
+ "uniform float IG_indexTextureWidth;\n" +
+ "uniform float IG_indexTextureHeight;\n" +
+ "uniform sampler2D IG_indexTexture;\n" +
+ "uniform sampler2D IG_coordinateTexture;\n" +
+ "uniform vec2 IG_implicitMeshSize;\n" +
+ //pop geometry
+ "uniform vec3 bgCenter;\n" +
+ "uniform vec3 bgSize;\n" +
+ "uniform float bgPrecisionMax;\n" +
+ "uniform float popGeometry;\n" +
+ "uniform float PG_precisionLevel;\n" +
+ "uniform float PG_powPrecision;\n" +
+ "uniform vec3 PG_maxBBSize;\n" +
+ "uniform vec3 PG_bbMin;\n" +
+ "uniform vec3 PG_bbMaxModF;\n" +
+ "uniform vec3 PG_bboxShiftVec;\n" +
+
+ //MAIN
+ "void main(void) {\n" +
+ " vec3 pos;\n" +
+ " if (imageGeometry != 0.0) {\n" +
+ //IG
+ " vec2 IG_texCoord;\n" +
+ " if(imageGeometry == 1.0) {\n" +
+ " vec2 halfPixel = vec2(0.5/IG_indexTextureWidth,0.5/IG_indexTextureHeight);\n" +
+ " IG_texCoord = vec2(position.x*(IG_implicitMeshSize.x/IG_indexTextureWidth), position.y*(IG_implicitMeshSize.y/IG_indexTextureHeight)) + halfPixel;\n" +
+ " vec2 IG_index = texture2D( IG_indexTexture, IG_texCoord ).rg;\n" +
+ " IG_texCoord = IG_index * 0.996108948;\n" +
+ " } else {\n" +
+ " vec2 halfPixel = vec2(0.5/IG_coordTextureWidth, 0.5/IG_coordTextureHeight);\n" +
+ " IG_texCoord = vec2(position.x*(IG_implicitMeshSize.x/IG_coordTextureWidth), position.y*(IG_implicitMeshSize.y/IG_coordTextureHeight)) + halfPixel;\n" +
+ " }\n" +
+ " pos = texture2D( IG_coordinateTexture, IG_texCoord ).rgb;\n" +
+ " pos = pos * (IG_bboxMax - IG_bboxMin) + IG_bboxMin;\n" +
+ " } else if (popGeometry != 0.0){\n" +
+ //PG
+ " pos = position;\n" +
+ " vec3 offsetVec = step(pos / bgPrecisionMax, PG_bbMaxModF) * PG_bboxShiftVec;\n" +
+ " if (PG_precisionLevel <= 2.0) {\n" +
+ " pos = floor(pos / PG_powPrecision) * PG_powPrecision;\n" +
+ " pos /= (65536.0 - PG_powPrecision);\n" +
+ " }\n" +
+ " else {\n" +
+ " pos /= bgPrecisionMax;\n" +
+ " }\n" +
+ " pos = (pos + offsetVec + PG_bbMin) * PG_maxBBSize;\n" +
+ " } else {\n" +
+ //BG
+ " pos = bgCenter + bgSize * position / bgPrecisionMax;\n" +
+ " }\n" +
+ " projCoords = modelViewProjectionMatrix * vec4(pos, 1.0);\n" +
+ " gl_Position = projCoords;\n" +
+ "}\n";
+ } else {
+ shader = "attribute vec3 position;\n" +
+ "uniform vec3 bgCenter;\n" +
+ "uniform vec3 bgSize;\n" +
+ "uniform float bgPrecisionMax;\n" +
+ "uniform mat4 modelViewProjectionMatrix;\n" +
+ "varying vec4 projCoords;\n" +
+
+ "void main(void) {\n" +
+ " vec3 pos = bgCenter + bgSize * position / bgPrecisionMax;\n" +
+ " projCoords = modelViewProjectionMatrix * vec4(pos, 1.0);\n" +
+ " gl_Position = projCoords;\n" +
+ "}\n";
+ }
+
+ var vertexShader = gl.createShader(gl.VERTEX_SHADER);
+ gl.shaderSource(vertexShader, shader);
+ gl.compileShader(vertexShader);
+
+ if(!gl.getShaderParameter(vertexShader, gl.COMPILE_STATUS)){
+ x3dom.debug.logError("[ShadowShader] VertexShader " + gl.getShaderInfoLog(vertexShader));
+ }
+
+ return vertexShader;
+};
+
+/**
+ * Generate the fragment shader
+ */
+x3dom.shader.ShadowShader.prototype.generateFragmentShader = function(gl)
+{
+ var shader = "#ifdef GL_FRAGMENT_PRECISION_HIGH\n";
+ shader += "precision highp float;\n";
+ shader += "#else\n";
+ shader += " precision mediump float;\n";
+ shader += "#endif\n\n";
+
+ shader += "varying vec4 projCoords;\n" +
+ "uniform float offset;\n" +
+ "uniform bool cameraView;\n";
+ if(!x3dom.caps.FP_TEXTURES || x3dom.caps.MOBILE)
+ shader += x3dom.shader.rgbaPacking();
+
+ shader += "void main(void) {\n" +
+ " vec3 proj = (projCoords.xyz / projCoords.w);\n";
+
+ if(!x3dom.caps.FP_TEXTURES || x3dom.caps.MOBILE) {
+ shader += "gl_FragColor = packDepth(proj.z);\n";
+ } else {
+ //use variance shadow maps, when not rendering from camera view
+ //shader += "if (!cameraView) proj.z = exp((1.0-offset)*80.0*proj.z);\n";
+ shader += " if (!cameraView){\n" +
+ " proj.z = (proj.z + 1.0)*0.5;\n" +
+ " proj.y = proj.z * proj.z;\n" +
+ " }\n";
+ shader += " gl_FragColor = vec4(proj, 1.0);\n";
+ }
+ shader += "}\n";
+
+ var fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
+ gl.shaderSource(fragmentShader, shader);
+ gl.compileShader(fragmentShader);
+
+ if(!gl.getShaderParameter(fragmentShader, gl.COMPILE_STATUS)){
+ x3dom.debug.logError("[ShadowShader] FragmentShader " + gl.getShaderInfoLog(fragmentShader));
+ }
+
+ return fragmentShader;
+};
+
+/*
+ * X3DOM JavaScript Library
+ * http://www.x3dom.org
+ *
+ * (C)2009 Fraunhofer IGD, Darmstadt, Germany
+ * Dual licensed under the MIT and GPL
+ *
+ * Based on code originally provided by
+ * Philip Taylor: http://philip.html5.org
+ */
+
+/**
+ * Generate the final Shader program
+ */
+x3dom.shader.ShadowRenderingShader = function(gl,shadowedLights)
+{
+ this.program = gl.createProgram();
+ var vertexShader = this.generateVertexShader(gl);
+ var fragmentShader = this.generateFragmentShader(gl,shadowedLights);
+
+ gl.attachShader(this.program, vertexShader);
+ gl.attachShader(this.program, fragmentShader);
+
+ // optional, but position should be at location 0 for performance reasons
+ gl.bindAttribLocation(this.program, 0, "position");
+
+ gl.linkProgram(this.program);
+
+ return this.program;
+};
+
+/**
+ * Generate the vertex shader
+ */
+x3dom.shader.ShadowRenderingShader.prototype.generateVertexShader = function(gl)
+{
+ var shader = "";
+ shader += "attribute vec2 position;\n";
+
+ shader += "varying vec2 vPosition;\n";
+
+ shader += "void main(void) {\n";
+ shader += " vPosition = position;\n";
+ shader += " gl_Position = vec4(position, -1.0, 1.0);\n";
+ shader += "}\n";
+
+ var vertexShader = gl.createShader(gl.VERTEX_SHADER);
+ gl.shaderSource(vertexShader, shader);
+ gl.compileShader(vertexShader);
+
+ if(!gl.getShaderParameter(vertexShader, gl.COMPILE_STATUS)){
+ x3dom.debug.logError("[ShadowRendering] VertexShader " + gl.getShaderInfoLog(vertexShader));
+ }
+
+ return vertexShader;
+};
+
+/**
+ * Generate the fragment shader
+ */
+x3dom.shader.ShadowRenderingShader.prototype.generateFragmentShader = function(gl,shadowedLights)
+{
+ var shader = "#ifdef GL_FRAGMENT_PRECISION_HIGH\n";
+ shader += "precision highp float;\n";
+ shader += "#else\n";
+ shader += " precision mediump float;\n";
+ shader += "#endif\n\n";
+
+ shader += "uniform mat4 inverseViewProj;\n";
+ shader += "uniform mat4 inverseProj;\n";
+ shader += "varying vec2 vPosition;\n";
+ shader += "uniform sampler2D sceneMap;\n";
+ for (var i=0; i<5; i++)
+ shader += "uniform float cascade"+i+"_Depth;\n";
+
+
+ for(var l=0; l<shadowedLights.length; l++) {
+ shader += "uniform float light"+l+"_On;\n" +
+ "uniform float light"+l+"_Type;\n" +
+ "uniform vec3 light"+l+"_Location;\n" +
+ "uniform vec3 light"+l+"_Direction;\n" +
+ "uniform vec3 light"+l+"_Attenuation;\n" +
+ "uniform float light"+l+"_Radius;\n" +
+ "uniform float light"+l+"_BeamWidth;\n" +
+ "uniform float light"+l+"_CutOffAngle;\n" +
+ "uniform float light"+l+"_ShadowIntensity;\n" +
+ "uniform float light"+l+"_ShadowOffset;\n" +
+ "uniform mat4 light"+l+"_ViewMatrix;\n";
+ for (var j=0; j<6; j++){
+ shader += "uniform mat4 light"+l+"_"+j+"_Matrix;\n";
+ shader += "uniform sampler2D light"+l+"_"+j+"_ShadowMap;\n";
+ }
+ for (var j=0; j<5; j++)
+ shader += "uniform float light"+l+"_"+j+"_Split;\n";
+
+
+ }
+ if (!x3dom.caps.FP_TEXTURES || x3dom.caps.MOBILE)
+ shader += x3dom.shader.rgbaPacking();
+
+ shader += x3dom.shader.shadowRendering();
+
+ shader += x3dom.shader.gammaCorrectionDecl({}); //TODO shader properties?
+
+ shader += "void main(void) {\n" +
+ " float shadowValue = 1.0;\n" +
+ " vec2 texCoordsSceneMap = (vPosition + 1.0)*0.5;\n" +
+ " vec4 projCoords = texture2D(sceneMap, texCoordsSceneMap);\n" +
+ " if (projCoords != vec4(1.0,1.0,1.0,0.0)){\n";
+ if (!x3dom.caps.FP_TEXTURES || x3dom.caps.MOBILE){
+ shader += " projCoords.z = unpackDepth(projCoords);\n" +
+ " projCoords.w = 1.0;\n";
+ }
+
+ //reconstruct world and view coordinates from scene map
+ shader += " projCoords = projCoords / projCoords.w;\n" +
+ " projCoords.xy = vPosition;\n" +
+ " vec4 eyeCoords = inverseProj*projCoords;\n" +
+ " vec4 worldCoords = inverseViewProj*projCoords;\n" +
+ " float lightInfluence = 0.0;\n";
+
+ for(var l=0; l<shadowedLights.length; l++) {
+ shader +=
+ " lightInfluence = getLightInfluence(light"+l+"_Type, light"+l+"_ShadowIntensity, light"+l+"_On, light"+l+"_Location, light"+l+"_Direction, " +
+ "light"+l+"_CutOffAngle, light"+l+"_BeamWidth, light"+l+"_Attenuation, light"+l+"_Radius, eyeCoords.xyz/eyeCoords.w);\n" +
+ " if (lightInfluence != 0.0){\n" +
+ " vec4 shadowMapValues;\n" +
+ " float viewSampleDepth;\n";
+
+
+ if (!x3dom.isa(shadowedLights[l], x3dom.nodeTypes.PointLight)){
+ shader += " getShadowValuesCascaded(shadowMapValues, viewSampleDepth, worldCoords, -eyeCoords.z/eyeCoords.w,"+
+ "light"+l+"_0_Matrix,light"+l+"_1_Matrix,light"+l+"_2_Matrix,light"+l+"_3_Matrix,light"+l+"_4_Matrix,light"+l+"_5_Matrix,"+
+ "light"+l+"_0_ShadowMap,light"+l+"_1_ShadowMap,light"+l+"_2_ShadowMap,light"+l+"_3_ShadowMap,"+
+ "light"+l+"_4_ShadowMap,light"+l+"_5_ShadowMap, light"+l+"_0_Split, light"+l+"_1_Split, light"+l+"_2_Split, light"+l+"_3_Split, \n"+
+ "light"+l+"_4_Split);\n";
+ } else {
+ shader += " getShadowValuesPointLight(shadowMapValues, viewSampleDepth, light"+l+"_Location, worldCoords, light"+l+"_ViewMatrix, "+
+ "light"+l+"_0_Matrix,light"+l+"_1_Matrix,light"+l+"_2_Matrix,light"+l+"_3_Matrix,light"+l+"_4_Matrix,light"+l+"_5_Matrix,"+
+ "light"+l+"_0_ShadowMap,light"+l+"_1_ShadowMap,light"+l+"_2_ShadowMap,light"+l+"_3_ShadowMap,"+
+ "light"+l+"_4_ShadowMap,light"+l+"_5_ShadowMap);\n";
+ }
+
+ if (!x3dom.caps.FP_TEXTURES || x3dom.caps.MOBILE)
+ shader += " shadowValue *= clamp(ESM(shadowMapValues.z, viewSampleDepth, light"+l+"_ShadowOffset), "+
+ " 1.0 - light"+l+"_ShadowIntensity*lightInfluence, 1.0);\n";
+ else
+ shader += " shadowValue *= clamp(VSM(shadowMapValues.zy, viewSampleDepth, light"+l+"_ShadowOffset), "+
+ " 1.0 - light"+l+"_ShadowIntensity*lightInfluence, 1.0);\n";
+ shader += " }\n";
+
+ }
+
+ shader += "}\n" +
+ // In principle we should fix the place where this is multplied in instead
+ // of overcompensating for the subsequent error from here. This way of doing
+ // gamma correction explots the rule that (a*b)^x = a^x * b^x (x being the
+ // gamma coefficient), i.e. the umbra is corrected for now, the penumbra
+ // is incorrect and full light is zero here so unaffected as well.
+ " gl_FragColor = " + x3dom.shader.encodeGamma({}, "vec4(shadowValue, shadowValue, shadowValue, 1.0)") + ";\n" +
+ "}\n";
+
+ var fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
+ gl.shaderSource(fragmentShader, shader);
+ gl.compileShader(fragmentShader);
+
+ if(!gl.getShaderParameter(fragmentShader, gl.COMPILE_STATUS)){
+ x3dom.debug.logError("[ShadowRendering] FragmentShader " + gl.getShaderInfoLog(fragmentShader));
+ }
+
+ return fragmentShader;
+};
+
+/*
+ * X3DOM JavaScript Library
+ * http://www.x3dom.org
+ *
+ * (C)2009 Fraunhofer IGD, Darmstadt, Germany
+ * Dual licensed under the MIT and GPL
+ *
+ * Based on code originally provided by
+ * Philip Taylor: http://philip.html5.org
+ */
+
+/**
+ * Generate the final shader program
+ */
+x3dom.shader.TextureRefinementShader = function (gl) {
+ this.program = gl.createProgram();
+
+ var vertexShader = this.generateVertexShader(gl);
+ var fragmentShader = this.generateFragmentShader(gl);
+
+ gl.attachShader(this.program, vertexShader);
+ gl.attachShader(this.program, fragmentShader);
+
+ // optional, but position should be at location 0 for performance reasons
+ gl.bindAttribLocation(this.program, 0, "position");
+
+ gl.linkProgram(this.program);
+
+ return this.program;
+};
+
+/**
+ * Generate the vertex shader
+ */
+x3dom.shader.TextureRefinementShader.prototype.generateVertexShader = function (gl) {
+ var shader = "attribute vec2 position;\n" +
+ "varying vec2 fragTexCoord;\n" +
+ "\n" +
+ "void main(void) {\n" +
+ " fragTexCoord = (position.xy + 1.0) / 2.0;\n" +
+ " gl_Position = vec4(position, -1.0, 1.0);\n" +
+ "}\n";
+
+ var vertexShader = gl.createShader(gl.VERTEX_SHADER);
+ gl.shaderSource(vertexShader, shader);
+ gl.compileShader(vertexShader);
+
+ if (!gl.getShaderParameter(vertexShader, gl.COMPILE_STATUS)) {
+ x3dom.debug.logError("[TextureRefinementShader] VertexShader " + gl.getShaderInfoLog(vertexShader));
+ }
+
+ return vertexShader;
+};
+
+/**
+ * Generate the fragment shader
+ */
+x3dom.shader.TextureRefinementShader.prototype.generateFragmentShader = function (gl) {
+ var shader = "#ifdef GL_FRAGMENT_PRECISION_HIGH\n" +
+ " precision highp float;\n" +
+ "#else\n" +
+ " precision mediump float;\n" +
+ "#endif\n\n";
+
+ shader += "uniform sampler2D stamp;\n" +
+ "uniform sampler2D lastTex;\n" +
+ "uniform sampler2D curTex;\n" +
+ "uniform int mode;\n" +
+ "uniform vec2 repeat;\n" +
+ "varying vec2 fragTexCoord;\n" +
+ "\n" +
+ "void init(void);\n" +
+ "void refine(void);\n" +
+ "\n" +
+ "void main(void) {\n" +
+ " if (mode == 0) { init(); }\n" +
+ " else { refine(); }\n" +
+ "}\n" +
+ "\n" +
+ "void init(void) {\n" +
+ " gl_FragColor = texture2D(curTex, fragTexCoord);\n" +
+ "}\n" +
+ "\n" +
+ "void refine(void) {\n" +
+ " vec3 red = texture2D(stamp, repeat * fragTexCoord).rgb;\n" +
+ " vec3 v1 = texture2D(lastTex, fragTexCoord).rgb;\n" +
+ " vec3 v2 = texture2D(curTex, fragTexCoord).rgb;\n" +
+ " if (red.r <= 0.5) {\n" +
+ " gl_FragColor = vec4(v1, 1.0);\n" +
+ " }\n" +
+ " else {\n" +
+ " gl_FragColor = vec4(v2, 1.0);\n" +
+ " }\n" +
+ "}\n";
+
+ var fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
+ gl.shaderSource(fragmentShader, shader);
+ gl.compileShader(fragmentShader);
+
+ if (!gl.getShaderParameter(fragmentShader, gl.COMPILE_STATUS)) {
+ x3dom.debug.logError("[TextureRefinementShader] FragmentShader " + gl.getShaderInfoLog(fragmentShader));
+ }
+
+ return fragmentShader;
+};
+
+/*
+ * X3DOM JavaScript Library
+ * http://www.x3dom.org
+ *
+ * (C)2009 Fraunhofer IGD, Darmstadt, Germany
+ * Dual licensed under the MIT and GPL
+ *
+ * Based on code originally provided by
+ * Philip Taylor: http://philip.html5.org
+ */
+
+/**
+ * Generate the final BlurShader program
+ * (gaussian blur for 3x3, 5x5 and 7x7 kernels)
+ */
+x3dom.shader.BlurShader = function(gl)
+{
+ this.program = gl.createProgram();
+
+ var vertexShader = this.generateVertexShader(gl);
+ var fragmentShader = this.generateFragmentShader(gl);
+
+ gl.attachShader(this.program, vertexShader);
+ gl.attachShader(this.program, fragmentShader);
+
+ // optional, but position should be at location 0 for performance reasons
+ gl.bindAttribLocation(this.program, 0, "position");
+
+ gl.linkProgram(this.program);
+
+ return this.program;
+};
+
+/**
+ * Generate the vertex shader
+ */
+x3dom.shader.BlurShader.prototype.generateVertexShader = function(gl)
+{
+ var shader = "";
+ shader += "attribute vec2 position;\n";
+
+ shader += "varying vec2 vPosition;\n";
+
+ shader += "void main(void) {\n";
+ shader += " vPosition = position;\n";
+ shader += " gl_Position = vec4(position, -1.0, 1.0);\n";
+ shader += "}\n";
+
+ var vertexShader = gl.createShader(gl.VERTEX_SHADER);
+ gl.shaderSource(vertexShader, shader);
+ gl.compileShader(vertexShader);
+
+ if(!gl.getShaderParameter(vertexShader, gl.COMPILE_STATUS)){
+ x3dom.debug.logError("[BlurShader] VertexShader " + gl.getShaderInfoLog(vertexShader));
+ }
+
+ return vertexShader;
+};
+
+/**
+ * Generate the fragment shader
+ */
+x3dom.shader.BlurShader.prototype.generateFragmentShader = function(gl)
+{
+ var shader = "#ifdef GL_FRAGMENT_PRECISION_HIGH\n";
+ shader += "precision highp float;\n";
+ shader += "#else\n";
+ shader += " precision mediump float;\n";
+ shader += "#endif\n\n";
+
+ shader += "varying vec2 vPosition;\n" +
+ "uniform sampler2D texture;\n" +
+ "uniform bool horizontal;\n" +
+ "uniform float pixelSizeHor;\n" +
+ "uniform float pixelSizeVert;\n" +
+ "uniform int filterSize;\n";
+
+ if (!x3dom.caps.FP_TEXTURES || x3dom.caps.MOBILE){
+ shader += x3dom.shader.rgbaPacking() +
+
+ "void main(void) {\n" +
+ " vec2 texCoords = (vPosition + 1.0)*0.5;\n" +
+ " vec2 offset;\n" +
+ " if (horizontal) offset = vec2(pixelSizeHor, 0.0);\n" +
+ " else offset = vec2(0.0, pixelSizeVert);\n" +
+ " float depth = unpackDepth(texture2D(texture, texCoords));\n" +
+ " if (filterSize == 3){\n" +
+ " depth = depth * 0.3844;\n" +
+ " depth += 0.3078*unpackDepth(texture2D(texture, texCoords-offset));\n" +
+ " depth += 0.3078*unpackDepth(texture2D(texture, texCoords+offset));\n" +
+ " } else if (filterSize == 5){\n" +
+ " depth = depth * 0.2921;\n" +
+ " depth += 0.2339*unpackDepth(texture2D(texture, texCoords-offset));\n" +
+ " depth += 0.2339*unpackDepth(texture2D(texture, texCoords+offset));\n" +
+ " depth += 0.1201*unpackDepth(texture2D(texture, texCoords-2.0*offset));\n" +
+ " depth += 0.1201*unpackDepth(texture2D(texture, texCoords+2.0*offset));\n" +
+ " } else if (filterSize == 7){\n" +
+ " depth = depth * 0.2161;\n" +
+ " depth += 0.1907*unpackDepth(texture2D(texture, texCoords-offset));\n" +
+ " depth += 0.1907*unpackDepth(texture2D(texture, texCoords+offset));\n" +
+ " depth += 0.1311*unpackDepth(texture2D(texture, texCoords-2.0*offset));\n" +
+ " depth += 0.1311*unpackDepth(texture2D(texture, texCoords+2.0*offset));\n" +
+ " depth += 0.0702*unpackDepth(texture2D(texture, texCoords-3.0*offset));\n" +
+ " depth += 0.0702*unpackDepth(texture2D(texture, texCoords+3.0*offset));\n" +
+ " }\n" +
+ " gl_FragColor = packDepth(depth);\n" +
+ "}\n";
+ } else{
+ shader += "void main(void) {\n" +
+ " vec2 texCoords = (vPosition + 1.0)*0.5;\n" +
+ " vec2 offset;\n" +
+ " if (horizontal) offset = vec2(pixelSizeHor, 0.0);\n" +
+ " else offset = vec2(0.0, pixelSizeVert);\n" +
+ " vec4 color = texture2D(texture, texCoords);\n" +
+ " if (filterSize == 3){\n" +
+ " color = color * 0.3844;\n" +
+ " color += 0.3078*texture2D(texture, texCoords-offset);\n" +
+ " color += 0.3078*texture2D(texture, texCoords+offset);\n" +
+ " } else if (filterSize == 5){\n" +
+ " color = color * 0.2921;\n" +
+ " color += 0.2339*texture2D(texture, texCoords-offset);\n" +
+ " color += 0.2339*texture2D(texture, texCoords+offset);\n" +
+ " color += 0.1201*texture2D(texture, texCoords-2.0*offset);\n" +
+ " color += 0.1201*texture2D(texture, texCoords+2.0*offset);\n" +
+ " } else if (filterSize == 7){\n" +
+ " color = color * 0.2161;\n" +
+ " color += 0.1907*texture2D(texture, texCoords-offset);\n" +
+ " color += 0.1907*texture2D(texture, texCoords+offset);\n" +
+ " color += 0.1311*texture2D(texture, texCoords-2.0*offset);\n" +
+ " color += 0.1311*texture2D(texture, texCoords+2.0*offset);\n" +
+ " color += 0.0702*texture2D(texture, texCoords-3.0*offset);\n" +
+ " color += 0.0702*texture2D(texture, texCoords+3.0*offset);\n" +
+ " }\n" +
+ " gl_FragColor = color;\n" +
+ "}\n";
+ }
+
+ var fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
+ gl.shaderSource(fragmentShader, shader);
+ gl.compileShader(fragmentShader);
+
+ if(!gl.getShaderParameter(fragmentShader, gl.COMPILE_STATUS)){
+ x3dom.debug.logError("[BlurShader] FragmentShader " + gl.getShaderInfoLog(fragmentShader));
+ }
+
+ return fragmentShader;
+};
+
+/**
+ * Generate the final ShadowShader program
+ */
+x3dom.shader.SSAOShader = function(gl)
+{
+ this.program = gl.createProgram();
+
+ var vertexShader = this.generateVertexShader(gl);
+ var fragmentShader = this.generateFragmentShader(gl);
+
+ gl.attachShader(this.program, vertexShader);
+ gl.attachShader(this.program, fragmentShader);
+
+ // optional, but position should be at location 0 for performance reasons
+ gl.bindAttribLocation(this.program, 0, "position");
+
+ gl.linkProgram(this.program);
+ return this.program;
+};
+
+/**
+ * Generate the vertex shader
+ */
+x3dom.shader.SSAOShader.prototype.generateVertexShader = function(gl)
+{
+ var shader = "attribute vec3 position;\n" +
+ "varying vec2 depthTexCoord;\n" +
+ "varying vec2 randomTexCoord;\n"+
+ "uniform vec2 randomTextureTilingFactor;\n"+
+ "\n" +
+ "void main(void) {\n" +
+ " vec2 texCoord = (position.xy + 1.0) * 0.5;\n" +
+ " depthTexCoord = texCoord;\n" +
+ " randomTexCoord = randomTextureTilingFactor*texCoord;\n"+
+ " gl_Position = vec4(position.xy, 0.0, 1.0);\n" +
+ "}\n";
+
+ var vertexShader = gl.createShader(gl.VERTEX_SHADER);
+ gl.shaderSource(vertexShader, shader);
+ gl.compileShader(vertexShader);
+
+ if(!gl.getShaderParameter(vertexShader, gl.COMPILE_STATUS)){
+ x3dom.debug.logError("[SSAOShader] VertexShader " + gl.getShaderInfoLog(vertexShader));
+ }
+
+ return vertexShader;
+};
+
+/**
+ * Returns the code for a function "getDepth(vec2 depthTexCoord)" that returns the linear depth.
+ * It also introduces two uniform floats depthReconstructionConstantA and depthReconstructionConstantB,
+ * which are needed for the depth reconstruction.
+ */
+x3dom.shader.SSAOShader.depthReconsructionFunctionCode = function()
+{
+ var code = "uniform float depthReconstructionConstantA;\n"+
+ "uniform float depthReconstructionConstantB;\n";
+ if (!x3dom.caps.FP_TEXTURES || x3dom.caps.MOBILE)
+ code += x3dom.shader.rgbaPacking();
+
+ code+= "float getDepth(vec2 depthTexCoord) {\n"+
+ " vec4 col = texture2D(depthTexture, depthTexCoord);\n"+
+ " float d;\n";
+
+ if (!x3dom.caps.FP_TEXTURES || x3dom.caps.MOBILE){
+ code+=" d = unpackDepth(col);\n";
+ } else {
+ code+=" d = col.b;\n"
+ }
+ code+= " return depthReconstructionConstantB/(depthReconstructionConstantA+d);\n";
+ code+= "}\n";
+ return code;
+}
+
+/**
+ * Generate the fragment shader
+ */
+x3dom.shader.SSAOShader.prototype.generateFragmentShader = function(gl)
+{
+ var shader = "#ifdef GL_FRAGMENT_PRECISION_HIGH\n";
+ shader += "precision highp float;\n";
+ shader += "#else\n";
+ shader += " precision mediump float;\n";
+ shader += "#endif\n\n";
+
+ shader += "uniform sampler2D depthTexture;\n" +
+ "uniform sampler2D randomTexture;\n"+
+ "uniform float nearPlane;\n"+
+ "uniform float farPlane;\n"+
+ "uniform float radius;\n"+
+ "uniform float depthBufferEpsilon;\n"+
+ "uniform vec3 samples[16];\n"+
+ "varying vec2 depthTexCoord;\n" +
+ "varying vec2 randomTexCoord;\n";
+
+ shader += x3dom.shader.SSAOShader.depthReconsructionFunctionCode();
+
+ shader+= "void main(void) {\n" +
+ " float referenceDepth = getDepth(depthTexCoord);\n" +
+ " if(referenceDepth == 1.0)\n"+ //background
+ " {\n"+
+ " gl_FragColor = vec4(1.0,1.0,1.0, 1.0);\n"+
+ " return;\n"+
+ " }\n"+
+ " int numOcclusions = 0;\n"+
+ " for(int i = 0; i<16; ++i){\n"+
+ " float scale = 1.0/referenceDepth;\n"+
+ " vec3 samplepos = reflect(samples[i],texture2D(randomTexture,randomTexCoord).xyz*2.0-vec3(1.0,1.0,1.0));\n"+
+ " float sampleDepth = getDepth(depthTexCoord+samplepos.xy*scale*radius);\n"+
+ " //if(abs(sampleDepth-referenceDepth)<=radius*(1.0/nearPlane))\n"+ //leads to bright halos
+ " if( sampleDepth < referenceDepth-depthBufferEpsilon) {\n"+
+ " ++numOcclusions;\n"+
+ " }\n"+
+ " }\n"+
+ " float r = 1.0-float(numOcclusions)/16.0;\n"+
+ " r*=2.0;\n"+
+ " gl_FragColor = vec4(r,r,r, 1.0);\n" +
+ "}\n";
+
+ var fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
+ gl.shaderSource(fragmentShader, shader);
+ gl.compileShader(fragmentShader);
+
+ if(!gl.getShaderParameter(fragmentShader, gl.COMPILE_STATUS)){
+ x3dom.debug.logError("[SSAOhader] FragmentShader " + gl.getShaderInfoLog(fragmentShader));
+ }
+
+ return fragmentShader;
+};
+
+
+/**
+ * Generate the final ShadowShader program
+ */
+x3dom.shader.SSAOBlurShader = function(gl)
+{
+ this.program = gl.createProgram();
+
+ var vertexShader = this.generateVertexShader(gl);
+ var fragmentShader = this.generateFragmentShader(gl);
+
+ gl.attachShader(this.program, vertexShader);
+ gl.attachShader(this.program, fragmentShader);
+
+ // optional, but position should be at location 0 for performance reasons
+ gl.bindAttribLocation(this.program, 0, "position");
+
+ gl.linkProgram(this.program);
+ return this.program;
+};
+
+/**
+ * Generate the vertex shader
+ */
+x3dom.shader.SSAOBlurShader.prototype.generateVertexShader = function(gl)
+{
+ var shader = "attribute vec3 position;\n" +
+ "varying vec2 fragTexCoord;\n" +
+ "\n" +
+ "void main(void) {\n" +
+ " vec2 texCoord = (position.xy + 1.0) * 0.5;\n" +
+ " fragTexCoord = texCoord;\n" +
+ " gl_Position = vec4(position.xy, 0.0, 1.0);\n" +
+ "}\n";
+
+ var vertexShader = gl.createShader(gl.VERTEX_SHADER);
+ gl.shaderSource(vertexShader, shader);
+ gl.compileShader(vertexShader);
+
+ if(!gl.getShaderParameter(vertexShader, gl.COMPILE_STATUS)){
+ x3dom.debug.logError("[SSAOShader] VertexShader " + gl.getShaderInfoLog(vertexShader));
+ }
+
+ return vertexShader;
+};
+
+/**
+ * Generate the fragment shader
+ */
+x3dom.shader.SSAOBlurShader.prototype.generateFragmentShader = function(gl)
+{
+ var shader = "#ifdef GL_FRAGMENT_PRECISION_HIGH\n";
+ shader += "precision highp float;\n";
+ shader += "#else\n";
+ shader += " precision mediump float;\n";
+ shader += "#endif\n\n";
+
+
+ shader += "uniform sampler2D SSAOTexture;\n" +
+ "uniform sampler2D depthTexture;\n" +
+ "uniform float nearPlane;\n"+
+ "uniform float farPlane;\n"+
+ "uniform float amount;\n"+
+ "uniform vec2 pixelSize;\n"+
+ "uniform float depthThreshold;\n"+
+ "varying vec2 fragTexCoord;\n";
+
+
+ shader += x3dom.shader.SSAOShader.depthReconsructionFunctionCode();
+
+ shader+="void main(void) {\n" +
+ " float sum = 0.0;\n"+
+ " float numSamples = 0.0;\n"+
+ " float referenceDepth = getDepth(fragTexCoord);\n"+
+ " for(int i = -2; i<2;i++){\n"+
+ " for(int j = -2; j<2;j++){\n"+
+ " vec2 sampleTexCoord = fragTexCoord+vec2(pixelSize.x*float(i),pixelSize.y*float(j));\n"+
+ " if(abs(referenceDepth - getDepth(sampleTexCoord))<depthThreshold){\n"+
+ " sum+= texture2D(SSAOTexture,sampleTexCoord).r;\n"+
+ " numSamples++;\n"+
+ " }}}\n"+
+ " float intensity = mix(1.0,sum/numSamples,amount);\n"+
+ " gl_FragColor = vec4(intensity,intensity,intensity,1.0);\n"+
+ "}\n";
+
+ var fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
+ gl.shaderSource(fragmentShader, shader);
+ gl.compileShader(fragmentShader);
+
+ if(!gl.getShaderParameter(fragmentShader, gl.COMPILE_STATUS)){
+ x3dom.debug.logError("[SSAOhader] FragmentShader " + gl.getShaderInfoLog(fragmentShader));
+ }
+
+ return fragmentShader;
+};
+
+
+x3dom.SSAO = {};
+x3dom.SSAO.isEnabled = function(scene){return scene.getEnvironment()._vf.SSAO};
+
+/**
+ * Reinitializes the shaders if they were not created yet or need to be updated.
+ */
+x3dom.SSAO.reinitializeShadersIfNecessary = function(gl){
+ if(x3dom.SSAO.shaderProgram === undefined){
+ x3dom.SSAO.shaderProgram = x3dom.Utils.wrapProgram(gl, new x3dom.shader.SSAOShader(gl), "ssao");
+ }
+ if(x3dom.SSAO.blurShaderProgram === undefined){
+ x3dom.SSAO.blurShaderProgram = x3dom.Utils.wrapProgram(gl, new x3dom.shader.SSAOBlurShader(gl), "ssao-blur");
+ }
+};
+
+/**
+ * Reinitializes the random Texture if it was not created yet or if it's size changed.
+ */
+x3dom.SSAO.reinitializeRandomTextureIfNecessary = function(gl,scene){
+ var sizeHasChanged = scene.getEnvironment()._vf.SSAOrandomTextureSize != x3dom.SSAO.currentRandomTextureSize;
+
+ if(x3dom.SSAO.randomTexture === undefined){
+ //create random texture
+ x3dom.SSAO.randomTexture = gl.createTexture();
+ }
+
+ if(x3dom.SSAO.randomTexture === undefined || sizeHasChanged){
+ gl.bindTexture(gl.TEXTURE_2D,x3dom.SSAO.randomTexture);
+ var rTexSize = x3dom.SSAO.currentRandomTextureSize = scene.getEnvironment()._vf.SSAOrandomTextureSize;
+ var randomImageBuffer = new ArrayBuffer(rTexSize*rTexSize*4); //rTexSize^2 pixels with 4 bytes each
+ var randomImageView = new Uint8Array(randomImageBuffer);
+ //fill the image with random unit Vectors in the z-y-plane:
+ for(var i = 0; i<rTexSize*rTexSize;++i){
+ var x = Math.random()*2.0-1.0;
+ var y = Math.random()*2.0-1.0;
+ var z = 0;
+ var length = Math.sqrt(x*x+y*y+z*z);
+ x /=length;
+ y /=length;
+ randomImageView[4*i] = (x+1.0)*0.5*255.0;
+ randomImageView[4*i+1] = (y+1.0)*0.5*255.0;
+ randomImageView[4*i+2] = (z+1.0)*0.5*255.0;
+ randomImageView[4*i+3] = 255;
+ }
+ gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA,rTexSize,rTexSize,0, gl.RGBA,gl.UNSIGNED_BYTE, randomImageView);
+ gl.bindTexture(gl.TEXTURE_2D,null);
+ }
+};
+
+/**
+ * Reinitializes the FBO render target for the SSAO if it wasn't created yet or if the canvas size changed.
+ */
+x3dom.SSAO.reinitializeFBOIfNecessary = function(gl,canvas){
+
+ var dimensionsHaveChanged =
+ x3dom.SSAO.currentFBOWidth != canvas.width ||
+ x3dom.SSAO.currentFBOHeight != canvas.height;
+
+ if(x3dom.SSAO.fbo === undefined || dimensionsHaveChanged)
+ {
+ x3dom.SSAO.currentFBOWidth = canvas.width;
+ x3dom.SSAO.currentFBOHeight = canvas.height;
+ var oldfbo = gl.getParameter(gl.FRAMEBUFFER_BINDING);
+ if(x3dom.SSAO.fbo === undefined){
+ //create framebuffer
+ x3dom.SSAO.fbo = gl.createFramebuffer();
+ }
+ gl.bindFramebuffer(gl.FRAMEBUFFER, x3dom.SSAO.fbo);
+ if(x3dom.SSAO.fbotex === undefined){
+ //create render texture
+ x3dom.SSAO.fbotex = gl.createTexture();
+ }
+ gl.bindTexture(gl.TEXTURE_2D,x3dom.SSAO.fbotex);
+ gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA,x3dom.SSAO.currentFBOWidth,
+ x3dom.SSAO.currentFBOHeight,0, gl.RGBA,gl.UNSIGNED_BYTE, null);
+ gl.bindTexture(gl.TEXTURE_2D,null);
+ gl.framebufferTexture2D(gl.FRAMEBUFFER,
+ gl.COLOR_ATTACHMENT0,
+ gl.TEXTURE_2D,
+ x3dom.SSAO.fbotex,
+ 0);
+ gl.bindFramebuffer(gl.FRAMEBUFFER, oldfbo);
+ }
+};
+
+/*
+ * Renders a sparsely sampled Screen-Space Ambient Occlusion Factor.
+ * @param stateManager x3dom webgl stateManager
+ * @param gl WebGL context
+ * @param scene Scene Node
+ * @param tex depth texture
+ * @param canvas Canvas the scene is rendered on (needed for dimensions)
+ * @param fbo FrameBufferObject handle that should be used as a target (null to use curent fbo)
+ */
+x3dom.SSAO.render = function(stateManager,gl,scene,tex,canvas,fbo) {
+ //save previous fbo
+ var oldfbo = gl.getParameter(gl.FRAMEBUFFER_BINDING);
+ if(fbo != null){
+ gl.bindFramebuffer(gl.FRAMEBUFFER, fbo);
+ }
+
+ stateManager.frontFace(gl.CCW);
+ stateManager.disable(gl.CULL_FACE);
+ stateManager.disable(gl.DEPTH_TEST);
+
+ var sp = x3dom.SSAO.shaderProgram;
+
+ stateManager.useProgram(sp);
+
+ //set up uniforms
+ sp.depthTexture = 0;
+ sp.randomTexture = 1;
+ sp.radius = scene.getEnvironment()._vf.SSAOradius;
+ sp.randomTextureTilingFactor = [canvas.width/x3dom.SSAO.currentRandomTextureSize,canvas.height/x3dom.SSAO.currentRandomTextureSize];
+
+ var viewpoint = scene.getViewpoint();
+ var nearPlane = viewpoint.getNear();
+ var farPlane = viewpoint.getFar();
+ sp.nearPlane = nearPlane;
+ sp.farPlane = farPlane;
+ sp.depthReconstructionConstantA = (farPlane+nearPlane)/(nearPlane-farPlane);
+ sp.depthReconstructionConstantB = (2.0*farPlane*nearPlane)/(nearPlane-farPlane);
+ sp.depthBufferEpsilon = 0.0001*(farPlane-nearPlane);
+ //16 samples with a well distributed pseudo random opposing-pairs sampling pattern:
+ sp.samples = [0.03800223814729654,0.10441029119843426,-0.04479934806797181,
+ -0.03800223814729654,-0.10441029119843426,0.04479934806797181,
+ -0.17023209847088397,0.1428416910414532,0.6154407640895228,
+ 0.17023209847088397,-0.1428416910414532,-0.6154407640895228,
+ -0.288675134594813,-0.16666666666666646,-0.3774214123135722,
+ 0.288675134594813,0.16666666666666646,0.3774214123135722,
+ 0.07717696785196887,-0.43769233467209245,-0.5201284112706428,
+ -0.07717696785196887,0.43769233467209245,0.5201284112706428,
+ 0.5471154183401156,-0.09647120981496134,-0.15886420745887797,
+ -0.5471154183401156,0.09647120981496134,0.15886420745887797,
+ 0.3333333333333342,0.5773502691896253,-0.8012446019636266,
+ -0.3333333333333342,-0.5773502691896253,0.8012446019636266,
+ -0.49994591864508653,0.5958123446480936,-0.15385106176844343,
+ 0.49994591864508653,-0.5958123446480936,0.15385106176844343,
+ -0.8352823295874743,-0.3040179051783715,0.7825440557226517,
+ 0.8352823295874743,0.3040179051783715,-0.7825440557226517];
+ if (!sp.tex) {
+ sp.tex = 0;
+ }
+
+ //depth texture
+ gl.activeTexture(gl.TEXTURE0);
+ gl.bindTexture(gl.TEXTURE_2D, tex);
+
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
+ 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);
+
+ //random texture:
+ gl.activeTexture(gl.TEXTURE1);
+ gl.bindTexture(gl.TEXTURE_2D, x3dom.SSAO.randomTexture);
+
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT);
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT);
+
+ gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, scene._fgnd._webgl.buffers[0]);
+ gl.bindBuffer(gl.ARRAY_BUFFER, scene._fgnd._webgl.buffers[1]);
+ gl.vertexAttribPointer(sp.position, 3, gl.FLOAT, false, 0, 0);
+ gl.enableVertexAttribArray(sp.position);
+
+ gl.drawElements(scene._fgnd._webgl.primType, scene._fgnd._webgl.indexes.length, gl.UNSIGNED_SHORT, 0);
+
+ gl.disableVertexAttribArray(sp.position);
+
+ gl.activeTexture(gl.TEXTURE0);
+ gl.bindTexture(gl.TEXTURE_2D, null);
+ gl.activeTexture(gl.TEXTURE1);
+ gl.bindTexture(gl.TEXTURE_2D, null);
+
+ //restore prevoius fbo
+ if(fbo != null){
+ gl.bindFramebuffer(gl.FRAMEBUFFER, oldfbo);
+ }
+};
+
+/**
+ * Applies a depth-aware averaging filter.
+ * @param stateManager x3dom webgl stateManager
+ * @param gl WebGL context
+ * @param scene Scene Node
+ * @param ssaoTexture texture that contains the SSAO factor
+ * @param depthTexture depth texture
+ * @param canvas Canvas the scene is rendered on (needed for dimensions)
+ * @param fbo FrameBufferObject handle that should be used as a target (null to use curent fbo)
+ */
+x3dom.SSAO.blur = function(stateManager,gl,scene,ssaoTexture,depthTexture,canvas,fbo) {
+
+ //save previous fbo
+ var oldfbo = gl.getParameter(gl.FRAMEBUFFER_BINDING);
+ if(fbo != null){
+ gl.bindFramebuffer(gl.FRAMEBUFFER, fbo);
+ }
+
+ stateManager.frontFace(gl.CCW);
+ stateManager.disable(gl.CULL_FACE);
+ stateManager.disable(gl.DEPTH_TEST);
+
+ var sp = x3dom.SSAO.blurShaderProgram;
+
+ stateManager.useProgram(sp);
+
+ sp.SSAOTexture = 0;
+ sp.depthTexture = 1;
+
+ sp.depthThreshold = scene.getEnvironment()._vf.SSAOblurDepthTreshold;
+
+ var viewpoint = scene.getViewpoint();
+ var nearPlane = viewpoint.getNear();
+ var farPlane = viewpoint.getFar();
+ sp.nearPlane = nearPlane;
+ sp.farPlane = farPlane;
+ sp.depthReconstructionConstantA = (farPlane+nearPlane)/(nearPlane-farPlane);
+ sp.depthReconstructionConstantB = (2.0*farPlane*nearPlane)/(nearPlane-farPlane);
+ sp.pixelSize = [1.0/canvas.width,1.0/canvas.height];
+ sp.amount = scene.getEnvironment()._vf.SSAOamount;
+
+ //ssao texture
+ gl.activeTexture(gl.TEXTURE0);
+ gl.bindTexture(gl.TEXTURE_2D, ssaoTexture);
+
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
+ 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);
+
+ //depth texture
+ gl.activeTexture(gl.TEXTURE1);
+ gl.bindTexture(gl.TEXTURE_2D, depthTexture);
+
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
+ 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.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, scene._fgnd._webgl.buffers[0]);
+ gl.bindBuffer(gl.ARRAY_BUFFER, scene._fgnd._webgl.buffers[1]);
+ gl.vertexAttribPointer(sp.position, 3, gl.FLOAT, false, 0, 0);
+ gl.enableVertexAttribArray(sp.position);
+
+ gl.drawElements(scene._fgnd._webgl.primType, scene._fgnd._webgl.indexes.length, gl.UNSIGNED_SHORT, 0);
+
+ gl.disableVertexAttribArray(sp.position);
+
+ gl.activeTexture(gl.TEXTURE0);
+ gl.bindTexture(gl.TEXTURE_2D, null);
+ gl.activeTexture(gl.TEXTURE1);
+ gl.bindTexture(gl.TEXTURE_2D, null);
+
+ //restore previous fbo
+ if(fbo != null){
+ gl.bindFramebuffer(gl.FRAMEBUFFER, oldfbo);
+ }
+};
+
+/**
+ * Renders Screen-Space Ambeint Occlusion multiplicatively on top of the scene.
+ * @param stateManager state manager for the WebGL context
+ * @param gl WebGL context
+ * @param scene current scene
+ * @param canvas canvas that the scene is rendered to
+ */
+x3dom.SSAO.renderSSAO = function(stateManager, gl, scene, canvas) {
+
+ //set up resources if they are non-existent or if they are outdated:
+ this.reinitializeShadersIfNecessary(gl);
+ this.reinitializeRandomTextureIfNecessary(gl,scene);
+ this.reinitializeFBOIfNecessary(gl,canvas);
+
+ stateManager.viewport(0,0,canvas.width, canvas.height);
+
+ //render SSAO into fbo
+ this.render(stateManager,gl, scene, scene._webgl.fboScene.tex,canvas,x3dom.SSAO.fbo);
+ //render blurred SSAO multiplicatively
+ gl.enable(gl.BLEND);
+ gl.blendFunc(gl.DST_COLOR, gl.ONE_MINUS_SRC_ALPHA);
+ this.blur(stateManager,gl, scene, x3dom.SSAO.fbotex,scene._webgl.fboScene.tex,canvas,null);
+ gl.disable(gl.BLEND);
+};
+
+/*
+ * X3DOM JavaScript Library
+ * http://www.x3dom.org
+ *
+ * (C)2009 Fraunhofer IGD, Darmstadt, Germany
+ * Dual licensed under the MIT and GPL
+ *
+ * Based on code originally provided by
+ * Philip Taylor: http://philip.html5.org
+ */
+
+
+x3dom.gfx_webgl = (function () {
+ "use strict";
+
+ /*****************************************************************************
+ * Context constructor
+ *****************************************************************************/
+ function Context(ctx3d, canvas, name, x3dElem) {
+ this.ctx3d = ctx3d;
+ this.canvas = canvas;
+ this.name = name;
+ this.x3dElem = x3dElem;
+ this.IG_PositionBuffer = null;
+ this.cache = new x3dom.Cache();
+ this.stateManager = new x3dom.StateManager(ctx3d);
+ }
+
+
+ /*****************************************************************************
+ * Return context name
+ *****************************************************************************/
+ Context.prototype.getName = function () {
+ return this.name;
+ };
+
+
+ /*****************************************************************************
+ * Setup the 3D context and init some things
+ *****************************************************************************/
+ function setupContext(canvas, forbidMobileShaders, forceMobileShaders, tryWebGL2, x3dElem) {
+ var validContextNames = ['webgl', 'experimental-webgl', 'moz-webgl', 'webkit-3d'];
+
+ if (tryWebGL2) {
+ validContextNames = ['experimental-webgl2'].concat(validContextNames);
+ }
+
+ var ctx = null;
+
+ // TODO; FIXME; this is an ugly hack, don't look for elements like this
+ // (e.g., Bindable nodes may only exist in backend etc.)
+ var envNodes = x3dElem.getElementsByTagName("Environment");
+ var ssaoEnabled = (envNodes && envNodes[0] && envNodes[0].hasAttribute("SSAO") &&
+ envNodes[0].getAttribute("SSAO").toLowerCase() === 'true') ? true : false;
+
+ // Context creation params
+ var ctxAttribs = {
+ alpha: true,
+ depth: true,
+ stencil: true,
+ antialias: !ssaoEnabled,
+ premultipliedAlpha: false,
+ preserveDrawingBuffer: true,
+ failIfMajorPerformanceCaveat : true
+ };
+
+ for (var i = 0; i < validContextNames.length; i++) {
+ try {
+ ctx = canvas.getContext(validContextNames[i], ctxAttribs);
+
+ //If context creation fails, retry the creation with failIfMajorPerformanceCaveat = false
+ if ( !ctx ) {
+ x3dom.caps.RENDERMODE = "SOFTWARE";
+ ctxAttribs.failIfMajorPerformanceCaveat = false;
+ ctx = canvas.getContext(validContextNames[i], ctxAttribs);
+ }
+
+ if (ctx) {
+ var newCtx = new Context(ctx, canvas, 'webgl', x3dElem);
+
+ try {
+ //Save CAPS
+ x3dom.caps.VENDOR = ctx.getParameter(ctx.VENDOR);
+ x3dom.caps.VERSION = ctx.getParameter(ctx.VERSION);
+ x3dom.caps.RENDERER = ctx.getParameter(ctx.RENDERER);
+ x3dom.caps.SHADING_LANGUAGE_VERSION = ctx.getParameter(ctx.SHADING_LANGUAGE_VERSION);
+ x3dom.caps.RED_BITS = ctx.getParameter(ctx.RED_BITS);
+ x3dom.caps.GREEN_BITS = ctx.getParameter(ctx.GREEN_BITS);
+ x3dom.caps.BLUE_BITS = ctx.getParameter(ctx.BLUE_BITS);
+ x3dom.caps.ALPHA_BITS = ctx.getParameter(ctx.ALPHA_BITS);
+ x3dom.caps.DEPTH_BITS = ctx.getParameter(ctx.DEPTH_BITS);
+ x3dom.caps.MAX_VERTEX_ATTRIBS = ctx.getParameter(ctx.MAX_VERTEX_ATTRIBS);
+ x3dom.caps.MAX_VERTEX_TEXTURE_IMAGE_UNITS = ctx.getParameter(ctx.MAX_VERTEX_TEXTURE_IMAGE_UNITS);
+ x3dom.caps.MAX_VARYING_VECTORS = ctx.getParameter(ctx.MAX_VARYING_VECTORS);
+ x3dom.caps.MAX_VERTEX_UNIFORM_VECTORS = ctx.getParameter(ctx.MAX_VERTEX_UNIFORM_VECTORS);
+ x3dom.caps.MAX_COMBINED_TEXTURE_IMAGE_UNITS = ctx.getParameter(ctx.MAX_COMBINED_TEXTURE_IMAGE_UNITS);
+ x3dom.caps.MAX_TEXTURE_SIZE = ctx.getParameter(ctx.MAX_TEXTURE_SIZE);
+ x3dom.caps.MAX_TEXTURE_IMAGE_UNITS = ctx.getParameter(ctx.MAX_TEXTURE_IMAGE_UNITS);
+ x3dom.caps.MAX_CUBE_MAP_TEXTURE_SIZE = ctx.getParameter(ctx.MAX_CUBE_MAP_TEXTURE_SIZE);
+ x3dom.caps.COMPRESSED_TEXTURE_FORMATS = ctx.getParameter(ctx.COMPRESSED_TEXTURE_FORMATS);
+ x3dom.caps.MAX_RENDERBUFFER_SIZE = ctx.getParameter(ctx.MAX_RENDERBUFFER_SIZE);
+ x3dom.caps.MAX_VIEWPORT_DIMS = ctx.getParameter(ctx.MAX_VIEWPORT_DIMS);
+ x3dom.caps.ALIASED_LINE_WIDTH_RANGE = ctx.getParameter(ctx.ALIASED_LINE_WIDTH_RANGE);
+ x3dom.caps.ALIASED_POINT_SIZE_RANGE = ctx.getParameter(ctx.ALIASED_POINT_SIZE_RANGE);
+ x3dom.caps.SAMPLES = ctx.getParameter(ctx.SAMPLES);
+ x3dom.caps.INDEX_UINT = ctx.getExtension("OES_element_index_uint");
+ x3dom.caps.FP_TEXTURES = ctx.getExtension("OES_texture_float");
+ x3dom.caps.FPL_TEXTURES = ctx.getExtension("OES_texture_float_linear");
+ x3dom.caps.STD_DERIVATIVES = ctx.getExtension("OES_standard_derivatives");
+ x3dom.caps.DRAW_BUFFERS = ctx.getExtension("WEBGL_draw_buffers");
+ x3dom.caps.DEBUGRENDERINFO = ctx.getExtension("WEBGL_debug_renderer_info");
+ x3dom.caps.EXTENSIONS = ctx.getSupportedExtensions();
+
+ if ( x3dom.caps.DEBUGRENDERINFO ) {
+ x3dom.caps.UNMASKED_RENDERER_WEBGL = ctx.getParameter( x3dom.caps.DEBUGRENDERINFO.UNMASKED_RENDERER_WEBGL );
+ x3dom.caps.UNMASKED_VENDOR_WEBGL = ctx.getParameter( x3dom.caps.DEBUGRENDERINFO.UNMASKED_VENDOR_WEBGL );
+ } else {
+ x3dom.caps.UNMASKED_RENDERER_WEBGL = "";
+ x3dom.caps.UNMASKED_VENDOR_WEBGL = "";
+ }
+
+ var extString = x3dom.caps.EXTENSIONS.toString().replace(/,/g, ", ");
+ x3dom.debug.logInfo(validContextNames[i] + " context found\nVendor: " + x3dom.caps.VENDOR +
+ " " + x3dom.caps.UNMASKED_VENDOR_WEBGL + ", Renderer: " + x3dom.caps.RENDERER +
+ " " + x3dom.caps.UNMASKED_RENDERER_WEBGL + ", " + "Version: " + x3dom.caps.VERSION + ", " +
+ "ShadingLangV.: " + x3dom.caps.SHADING_LANGUAGE_VERSION
+ + ", MSAA samples: " + x3dom.caps.SAMPLES + "\nExtensions: " + extString);
+
+ if (x3dom.caps.INDEX_UINT) {
+ x3dom.Utils.maxIndexableCoords = 4294967295;
+ }
+
+ x3dom.caps.MOBILE = (function (a) {
+ return (/android.+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|symbian|treo|up\.(browser|link)|vodafone|wap|windows (ce|phone)|xda|xiino/i.test(a) || /1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|e\-|e\/|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(di|rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|xda(\-|2|g)|yas\-|your|zeto|zte\-/i.test(a.substr(0, 4)))
+ })(navigator.userAgent || navigator.vendor || window.opera);
+
+ // explicitly disable for iPad and the like
+ if (x3dom.caps.RENDERER.indexOf("PowerVR") >= 0 ||
+ navigator.appVersion.indexOf("Mobile") > -1 ||
+ // coarse guess to find out old SM 2.0 hardware (e.g. Intel):
+ x3dom.caps.MAX_VARYING_VECTORS <= 8 ||
+ x3dom.caps.MAX_VERTEX_TEXTURE_IMAGE_UNITS < 2) {
+ x3dom.caps.MOBILE = true;
+ }
+
+ if (x3dom.caps.MOBILE) {
+ if (forbidMobileShaders) {
+ x3dom.caps.MOBILE = false;
+ x3dom.debug.logWarning("Detected mobile graphics card! " +
+ "But being forced to desktop shaders which might not work!");
+ }
+ else {
+ x3dom.debug.logWarning("Detected mobile graphics card! " +
+ "Using low quality shaders without ImageGeometry support!");
+ }
+ }
+ else {
+ if (forceMobileShaders) {
+ x3dom.caps.MOBILE = true;
+ x3dom.debug.logWarning("Detected desktop graphics card! " +
+ "But being forced to mobile shaders with lower quality!");
+ }
+ }
+ }
+ catch (ex) {
+ x3dom.debug.logWarning(
+ "Your browser probably supports an older WebGL version. " +
+ "Please try the old mobile runtime instead:\n" +
+ "http://www.x3dom.org/x3dom/src_mobile/x3dom.js");
+ newCtx = null;
+ }
+
+ return newCtx;
+ }
+ }
+ catch (e) { x3dom.debug.logWarning(e); }
+ }
+ return null;
+ }
+
+
+ /*****************************************************************************
+ * Setup GL objects for given shape
+ *****************************************************************************/
+ Context.prototype.setupShape = function (gl, drawable, viewarea) {
+ var q = 0, q6;
+ var textures, t;
+ var vertices, positionBuffer;
+ var texCoordBuffer, normalBuffer, colorBuffer;
+ var indicesBuffer, indexArray;
+
+ var shape = drawable.shape;
+ var geoNode = shape._cf.geometry.node;
+
+ if (shape._webgl !== undefined) {
+ var needFullReInit = false;
+
+ // TODO; do same for texcoords etc.!
+ if (shape._dirty.colors === true &&
+ shape._webgl.shader.color === undefined && geoNode._mesh._colors[0].length) {
+ // required since otherwise shape._webgl.shader.color stays undefined
+ // and thus the wrong shader will be chosen although there are colors
+ needFullReInit = true;
+ }
+
+ // cleanup vertex buffer objects
+ if (needFullReInit && shape._cleanupGLObjects) {
+ shape._cleanupGLObjects(true, false);
+ }
+
+ //Check for dirty Textures
+ if (shape._dirty.texture === true) {
+ //Check for Texture add or remove
+ if (shape._webgl.texture.length != shape.getTextures().length) {
+ //Delete old Textures
+ for (t = 0; t < shape._webgl.texture.length; ++t) {
+ shape._webgl.texture.pop();
+ }
+
+ //Generate new Textures
+ textures = shape.getTextures();
+
+ for (t = 0; t < textures.length; ++t) {
+ shape._webgl.texture.push(new x3dom.Texture(gl, shape._nameSpace.doc, this.cache, textures[t]));
+ }
+
+ //Set dirty shader
+ shape._dirty.shader = true;
+
+ //Set dirty texture Coordinates
+ if (shape._webgl.shader.texcoord === undefined)
+ shape._dirty.texcoords = true;
+ }
+ else {
+ //If someone remove and append at the same time, texture count don't change
+ //and we have to check if all nodes the same as before
+ textures = shape.getTextures();
+
+ for (t = 0; t < textures.length; ++t) {
+ if (textures[t] === shape._webgl.texture[t].node) {
+ //only update the texture
+ shape._webgl.texture[t].update();
+ }
+ else {
+ //Set texture to null for recreation
+ shape._webgl.texture[t].texture = null;
+
+ //Set new node
+ shape._webgl.texture[t].node = textures[t];
+
+ //Update new node
+ shape._webgl.texture[t].update();
+ }
+ }
+ }
+ shape._dirty.texture = false;
+ }
+
+ //Check if we need a new shader
+ shape._webgl.shader = this.cache.getShaderByProperties(gl, shape, shape.getShaderProperties(viewarea));
+
+ if (!needFullReInit && shape._webgl.binaryGeometry == 0) // THINKABOUTME: What about PopGeo & Co.?
+ {
+ for (q = 0; q < shape._webgl.positions.length; q++)
+ {
+ q6 = 6 * q;
+
+ if (shape._dirty.positions == true || shape._dirty.indexes == true) {
+ if (shape._webgl.shader.position !== undefined) {
+ shape._webgl.indexes[q] = geoNode._mesh._indices[q];
+
+ gl.deleteBuffer(shape._webgl.buffers[q6]);
+
+ indicesBuffer = gl.createBuffer();
+ shape._webgl.buffers[q6] = indicesBuffer;
+
+ // explicitly check first positions array for consistency
+ if (x3dom.caps.INDEX_UINT && (geoNode._mesh._positions[0].length / 3 > 65535)) {
+ indexArray = new Uint32Array(shape._webgl.indexes[q]);
+ shape._webgl.indexType = gl.UNSIGNED_INT;
+ }
+ else {
+ indexArray = new Uint16Array(shape._webgl.indexes[q]);
+ shape._webgl.indexType = gl.UNSIGNED_SHORT;
+ }
+
+ gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indicesBuffer);
+ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indexArray, gl.STATIC_DRAW);
+
+ indexArray = null;
+
+ // vertex positions
+ shape._webgl.positions[q] = geoNode._mesh._positions[q];
+
+ // TODO; don't delete VBO but use glMapBuffer() and DYNAMIC_DRAW
+ gl.deleteBuffer(shape._webgl.buffers[q6 + 1]);
+
+ positionBuffer = gl.createBuffer();
+ shape._webgl.buffers[q6 + 1] = positionBuffer;
+
+ gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
+ gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, shape._webgl.buffers[q6]);
+
+ vertices = new Float32Array(shape._webgl.positions[q]);
+
+ gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);
+ gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
+
+ gl.vertexAttribPointer(shape._webgl.shader.position,
+ geoNode._mesh._numPosComponents,
+ shape._webgl.coordType, false,
+ shape._coordStrideOffset[0], shape._coordStrideOffset[1]);
+
+ vertices = null;
+ }
+
+ shape._dirty.positions = false;
+ shape._dirty.indexes = false;
+ }
+
+ if (shape._dirty.colors == true) {
+ if (shape._webgl.shader.color !== undefined) {
+ shape._webgl.colors[q] = geoNode._mesh._colors[q];
+
+ gl.deleteBuffer(shape._webgl.buffers[q6 + 4]);
+
+ colorBuffer = gl.createBuffer();
+ shape._webgl.buffers[q6 + 4] = colorBuffer;
+
+ colors = new Float32Array(shape._webgl.colors[q]);
+
+ gl.bindBuffer(gl.ARRAY_BUFFER, colorBuffer);
+ gl.bufferData(gl.ARRAY_BUFFER, colors, gl.STATIC_DRAW);
+
+ gl.vertexAttribPointer(shape._webgl.shader.color,
+ geoNode._mesh._numColComponents,
+ shape._webgl.colorType, false,
+ shape._colorStrideOffset[0], shape._colorStrideOffset[1]);
+
+ colors = null;
+ }
+
+ shape._dirty.colors = false;
+ }
+
+ if (shape._dirty.normals == true) {
+ if (shape._webgl.shader.normal !== undefined) {
+ shape._webgl.normals[q] = geoNode._mesh._normals[q];
+
+ gl.deleteBuffer(shape._webgl.buffers[q6 + 2]);
+
+ normalBuffer = gl.createBuffer();
+ shape._webgl.buffers[q6 + 2] = normalBuffer;
+
+ normals = new Float32Array(shape._webgl.normals[q]);
+
+ gl.bindBuffer(gl.ARRAY_BUFFER, normalBuffer);
+ gl.bufferData(gl.ARRAY_BUFFER, normals, gl.STATIC_DRAW);
+
+ gl.vertexAttribPointer(shape._webgl.shader.normal,
+ geoNode._mesh._numNormComponents,
+ shape._webgl.normalType, false,
+ shape._normalStrideOffset[0], shape._normalStrideOffset[1]);
+
+ normals = null;
+ }
+
+ shape._dirty.normals = false;
+ }
+
+ if (shape._dirty.texcoords == true) {
+ if (shape._webgl.shader.texcoord !== undefined) {
+ shape._webgl.texcoords[q] = geoNode._mesh._texCoords[q];
+
+ gl.deleteBuffer(shape._webgl.buffers[q6 + 3]);
+
+ texCoordBuffer = gl.createBuffer();
+ shape._webgl.buffers[q6 + 3] = texCoordBuffer;
+
+ texCoords = new Float32Array(shape._webgl.texcoords[q]);
+
+ gl.bindBuffer(gl.ARRAY_BUFFER, texCoordBuffer);
+ gl.bufferData(gl.ARRAY_BUFFER, texCoords, gl.STATIC_DRAW);
+
+ gl.vertexAttribPointer(shape._webgl.shader.texCoord,
+ geoNode._mesh._numTexComponents,
+ shape._webgl.texCoordType, false,
+ shape._texCoordStrideOffset[0], shape._texCoordStrideOffset[1]);
+
+ texCoords = null;
+ }
+
+ shape._dirty.texcoords = false;
+ }
+
+ if (shape._dirty.specialAttribs == true) {
+ if (shape._webgl.shader.particleSize !== undefined) {
+ var szArr = geoNode._vf.size.toGL();
+
+ if (szArr.length) {
+ gl.deleteBuffer(shape._webgl.buffers[q6 + 5]);
+ shape._webgl.buffers[q6 + 5] = gl.createBuffer();
+
+ gl.bindBuffer(gl.ARRAY_BUFFER, shape._webgl.buffers[q6 + 5]);
+ gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(szArr), gl.STATIC_DRAW);
+ }
+
+ shape._dirty.specialAttribs = false;
+ }
+ // Maybe other special attribs here, though e.g. AFAIK only BG (which not handled here) has ids.
+ }
+ }
+ }
+ else
+ {
+ // TODO; does not yet work with shared objects
+ /*
+ var spOld = shape._webgl.shader;
+ if (shape._cleanupGLObjects && needFullReInit)
+ shape._cleanupGLObjects(true, false);
+
+ // complete setup is sort of brute force, thus optimize!
+ x3dom.BinaryContainerLoader.setupBinGeo(shape, spOld, gl, viewarea, this);
+ shape.unsetGeoDirty();
+ */
+ }
+
+ if (shape._webgl.imageGeometry != 0) {
+ for (t = 0; t < shape._webgl.texture.length; ++t) {
+ shape._webgl.texture[t].updateTexture();
+ }
+
+ geoNode.unsetGeoDirty();
+ shape.unsetGeoDirty();
+ }
+
+ if (!needFullReInit) {
+ // we're done
+ return;
+ }
+ }
+ else if (!(x3dom.isa(geoNode, x3dom.nodeTypes.Text) ||
+ x3dom.isa(geoNode, x3dom.nodeTypes.BinaryGeometry) ||
+ x3dom.isa(geoNode, x3dom.nodeTypes.PopGeometry) ||
+ x3dom.isa(geoNode, x3dom.nodeTypes.ExternalGeometry)) &&
+ (!geoNode || geoNode._mesh._positions[0].length < 1))
+ {
+ if (x3dom.caps.MAX_VERTEX_TEXTURE_IMAGE_UNITS < 2 &&
+ x3dom.isa(geoNode, x3dom.nodeTypes.ImageGeometry)) {
+ x3dom.debug.logError("Can't render ImageGeometry nodes with only " +
+ x3dom.caps.MAX_VERTEX_TEXTURE_IMAGE_UNITS +
+ " vertex texture units. Please upgrade your GPU!");
+ }
+ else {
+ x3dom.debug.logError("NO VALID MESH OR NO VERTEX POSITIONS SET!");
+ }
+ return;
+ }
+
+ // we're on init, thus reset all dirty flags
+ shape.unsetDirty();
+
+ // dynamically attach clean-up method for GL objects
+ if (!shape._cleanupGLObjects)
+ {
+ shape._cleanupGLObjects = function (force, delGL)
+ {
+ // FIXME; what if complete tree is removed? Then _parentNodes.length may be greater 0.
+ if (this._webgl && ((arguments.length > 0 && force) || this._parentNodes.length == 0))
+ {
+ var sp = this._webgl.shader;
+
+ for (var q = 0; q < this._webgl.positions.length; q++) {
+ var q6 = 6 * q;
+
+ if (sp.position !== undefined) {
+ gl.deleteBuffer(this._webgl.buffers[q6 + 1]);
+ gl.deleteBuffer(this._webgl.buffers[q6]);
+ }
+
+ if (sp.normal !== undefined) {
+ gl.deleteBuffer(this._webgl.buffers[q6 + 2]);
+ }
+
+ if (sp.texcoord !== undefined) {
+ gl.deleteBuffer(this._webgl.buffers[q6 + 3]);
+ }
+
+ if (sp.color !== undefined) {
+ gl.deleteBuffer(this._webgl.buffers[q6 + 4]);
+ }
+
+ if (sp.id !== undefined) {
+ gl.deleteBuffer(this._webgl.buffers[q6 + 5]);
+ }
+ }
+
+ for (var df = 0; df < this._webgl.dynamicFields.length; df++) {
+ var attrib = this._webgl.dynamicFields[df];
+
+ if (sp[attrib.name] !== undefined) {
+ gl.deleteBuffer(attrib.buf);
+ }
+ }
+
+ if (delGL === undefined)
+ delGL = true;
+
+ if (delGL) {
+ delete this._webgl;
+
+ // be optimistic, one shape removed makes room for another one
+ x3dom.BinaryContainerLoader.outOfMemory = false;
+ }
+ }
+ }; // shape._cleanupGLObjects()
+ }
+
+
+ shape._webgl = {
+ positions: geoNode._mesh._positions,
+ normals: geoNode._mesh._normals,
+ texcoords: geoNode._mesh._texCoords,
+ colors: geoNode._mesh._colors,
+ indexes: geoNode._mesh._indices,
+ //indicesBuffer,positionBuffer,normalBuffer,texcBuffer,colorBuffer
+ //buffers: [{},{},{},{},{}],
+ indexType: gl.UNSIGNED_SHORT,
+ coordType: gl.FLOAT,
+ normalType: gl.FLOAT,
+ texCoordType: gl.FLOAT,
+ colorType: gl.FLOAT,
+ texture: [],
+ dirtyLighting: x3dom.Utils.checkDirtyLighting(viewarea),
+ imageGeometry: 0, // 0 := no IG, 1 := indexed IG, -1 := non-indexed IG
+ binaryGeometry: 0, // 0 := no BG, 1 := indexed BG, -1 := non-indexed BG
+ popGeometry: 0, // 0 : no PG, 1 : indexed PG, -1 : non-indexed PG
+ externalGeometry: 0 // 0 : no EG, 1 : indexed EG, -1 : non-indexed EG
+ };
+
+ //Set Textures
+ textures = shape.getTextures();
+ for (t = 0; t < textures.length; ++t) {
+ shape._webgl.texture.push(new x3dom.Texture(gl, shape._nameSpace.doc, this.cache, textures[t]));
+ }
+
+ //Set Shader
+ //shape._webgl.shader = this.cache.getDynamicShader(gl, viewarea, shape);
+ //shape._webgl.shader = this.cache.getShaderByProperties(gl, drawable.properties);
+ shape._webgl.shader = this.cache.getShaderByProperties(gl, shape, shape.getShaderProperties(viewarea));
+
+ // init vertex attribs
+ var sp = shape._webgl.shader;
+ var currAttribs = 0;
+
+ shape._webgl.buffers = [];
+ shape._webgl.dynamicFields = [];
+
+ //Set Geometry Primitive Type
+ if (x3dom.isa(geoNode, x3dom.nodeTypes.X3DBinaryContainerGeometryNode))
+ {
+ shape._webgl.primType = [];
+
+ for (var primCnt = 0; primCnt < geoNode._vf.primType.length; ++primCnt)
+ {
+ shape._webgl.primType.push(x3dom.Utils.primTypeDic(gl, geoNode._vf.primType[primCnt]));
+ }
+ }
+ else
+ {
+ shape._webgl.primType = x3dom.Utils.primTypeDic(gl, geoNode._mesh._primType);
+ }
+
+ // Binary container geometries need special handling
+ if (x3dom.isa(geoNode, x3dom.nodeTypes.ExternalGeometry))
+ {
+ geoNode.updateRenderData(shape, sp, gl, viewarea, this);
+ }
+ else if (x3dom.isa(geoNode, x3dom.nodeTypes.BinaryGeometry))
+ {
+ x3dom.BinaryContainerLoader.setupBinGeo(shape, sp, gl, viewarea, this);
+ }
+ else if (x3dom.isa(geoNode, x3dom.nodeTypes.PopGeometry))
+ {
+ x3dom.BinaryContainerLoader.setupPopGeo(shape, sp, gl, viewarea, this);
+ }
+ else if (x3dom.isa(geoNode, x3dom.nodeTypes.ImageGeometry))
+ {
+ x3dom.BinaryContainerLoader.setupImgGeo(shape, sp, gl, viewarea, this);
+ }
+ else // No special BinaryMesh, but IFS or similar
+ {
+ for (q = 0; q < shape._webgl.positions.length; q++)
+ {
+ q6 = 6 * q;
+
+ if (sp.position !== undefined) {
+ // bind indices for drawElements() call
+ indicesBuffer = gl.createBuffer();
+ shape._webgl.buffers[q6] = indicesBuffer;
+
+ // explicitly check first positions array for consistency
+ if (x3dom.caps.INDEX_UINT && (shape._webgl.positions[0].length / 3 > 65535)) {
+ indexArray = new Uint32Array(shape._webgl.indexes[q]);
+ shape._webgl.indexType = gl.UNSIGNED_INT;
+ }
+ else {
+ indexArray = new Uint16Array(shape._webgl.indexes[q]);
+ shape._webgl.indexType = gl.UNSIGNED_SHORT;
+ }
+
+ gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indicesBuffer);
+ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indexArray, gl.STATIC_DRAW);
+
+ indexArray = null;
+
+ positionBuffer = gl.createBuffer();
+ shape._webgl.buffers[q6 + 1] = positionBuffer;
+ gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
+
+ vertices = new Float32Array(shape._webgl.positions[q]);
+
+ gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);
+ gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
+
+ gl.vertexAttribPointer(sp.position,
+ geoNode._mesh._numPosComponents,
+ shape._webgl.coordType, false,
+ shape._coordStrideOffset[0], shape._coordStrideOffset[1]);
+ gl.enableVertexAttribArray(sp.position);
+
+ vertices = null;
+ }
+ if (sp.normal !== undefined || shape._webgl.normals[q]) {
+ normalBuffer = gl.createBuffer();
+ shape._webgl.buffers[q6 + 2] = normalBuffer;
+
+ var normals = new Float32Array(shape._webgl.normals[q]);
+
+ gl.bindBuffer(gl.ARRAY_BUFFER, normalBuffer);
+ gl.bufferData(gl.ARRAY_BUFFER, normals, gl.STATIC_DRAW);
+
+ gl.vertexAttribPointer(sp.normal,
+ geoNode._mesh._numNormComponents,
+ shape._webgl.normalType, false,
+ shape._normalStrideOffset[0], shape._normalStrideOffset[1]);
+ gl.enableVertexAttribArray(sp.normal);
+
+ normals = null;
+ }
+ if (sp.texcoord !== undefined) {
+ var texcBuffer = gl.createBuffer();
+ shape._webgl.buffers[q6 + 3] = texcBuffer;
+
+ var texCoords = new Float32Array(shape._webgl.texcoords[q]);
+
+ gl.bindBuffer(gl.ARRAY_BUFFER, texcBuffer);
+ gl.bufferData(gl.ARRAY_BUFFER, texCoords, gl.STATIC_DRAW);
+
+ gl.vertexAttribPointer(sp.texcoord,
+ geoNode._mesh._numTexComponents,
+ shape._webgl.texCoordType, false,
+ shape._texCoordStrideOffset[0], shape._texCoordStrideOffset[1]);
+ gl.enableVertexAttribArray(sp.texcoord);
+
+ texCoords = null;
+ }
+ if (sp.color !== undefined) {
+ colorBuffer = gl.createBuffer();
+ shape._webgl.buffers[q6 + 4] = colorBuffer;
+
+ var colors = new Float32Array(shape._webgl.colors[q]);
+
+ gl.bindBuffer(gl.ARRAY_BUFFER, colorBuffer);
+ gl.bufferData(gl.ARRAY_BUFFER, colors, gl.STATIC_DRAW);
+
+ gl.vertexAttribPointer(sp.color,
+ geoNode._mesh._numColComponents,
+ shape._webgl.colorType, false,
+ shape._colorStrideOffset[0], shape._colorStrideOffset[1]);
+ gl.enableVertexAttribArray(sp.color);
+
+ colors = null;
+ }
+ if (sp.particleSize !== undefined) {
+ var sizeArr = geoNode._vf.size.toGL();
+
+ if (sizeArr.length) {
+ var sizeBuffer = gl.createBuffer();
+ shape._webgl.buffers[q6 + 5] = sizeBuffer;
+
+ gl.bindBuffer(gl.ARRAY_BUFFER, sizeBuffer);
+ gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(sizeArr), gl.STATIC_DRAW);
+ }
+ }
+ }
+
+ // TODO; FIXME; handle geometry with split mesh that has dynamic fields!
+ for (var df in geoNode._mesh._dynamicFields)
+ {
+ if (!geoNode._mesh._dynamicFields.hasOwnProperty(df))
+ continue;
+
+ var attrib = geoNode._mesh._dynamicFields[df];
+
+ shape._webgl.dynamicFields[currAttribs] = {
+ buf: {}, name: df, numComponents: attrib.numComponents };
+
+ if (sp[df] !== undefined) {
+ var attribBuffer = gl.createBuffer();
+ shape._webgl.dynamicFields[currAttribs++].buf = attribBuffer;
+
+ var attribs = new Float32Array(attrib.value);
+
+ gl.bindBuffer(gl.ARRAY_BUFFER, attribBuffer);
+ gl.bufferData(gl.ARRAY_BUFFER, attribs, gl.STATIC_DRAW);
+
+ gl.vertexAttribPointer(sp[df], attrib.numComponents, gl.FLOAT, false, 0, 0);
+
+ attribs = null;
+ }
+ }
+ } // Standard geometry
+ };
+
+
+ /*****************************************************************************
+ * Mainly manages rendering of backgrounds and buffer clearing
+ *****************************************************************************/
+ Context.prototype.setupScene = function (gl, bgnd) {
+ var sphere = null;
+ var texture = null;
+
+ var that = this;
+
+ if (bgnd._webgl !== undefined) {
+ if (!bgnd._dirty) {
+ return;
+ }
+
+ if (bgnd._webgl.texture !== undefined && bgnd._webgl.texture) {
+ gl.deleteTexture(bgnd._webgl.texture);
+ }
+ if (bgnd._cleanupGLObjects) {
+ bgnd._cleanupGLObjects();
+ }
+ bgnd._webgl = {};
+ }
+
+ bgnd._dirty = false;
+
+ var url = bgnd.getTexUrl();
+ var i = 0;
+ var w = 1, h = 1;
+
+ if (url.length > 0 && url[0].length > 0) {
+ if (url.length >= 6 && url[1].length > 0 && url[2].length > 0 &&
+ url[3].length > 0 && url[4].length > 0 && url[5].length > 0) {
+ sphere = new x3dom.nodeTypes.Sphere();
+
+ bgnd._webgl = {
+ positions: sphere._mesh._positions[0],
+ indexes: sphere._mesh._indices[0],
+ buffers: [
+ {}, {}
+ ]
+ };
+
+ bgnd._webgl.primType = gl.TRIANGLES;
+
+ bgnd._webgl.shader = this.cache.getShader(gl, x3dom.shader.BACKGROUND_CUBETEXTURE);
+
+ bgnd._webgl.texture = x3dom.Utils.createTextureCube(gl, bgnd._nameSpace.doc, url,
+ true, bgnd._vf.crossOrigin, true, false);
+ }
+ else {
+ bgnd._webgl = {
+ positions: [-w, -h, 0, -w, h, 0, w, -h, 0, w, h, 0],
+ indexes: [0, 1, 2, 3],
+ buffers: [
+ {}, {}
+ ]
+ };
+
+ url = bgnd._nameSpace.getURL(url[0]);
+
+ bgnd._webgl.texture = x3dom.Utils.createTexture2D(gl, bgnd._nameSpace.doc, url,
+ true, bgnd._vf.crossOrigin, true, false);
+
+ bgnd._webgl.primType = gl.TRIANGLE_STRIP;
+
+ bgnd._webgl.shader = this.cache.getShader(gl, x3dom.shader.BACKGROUND_TEXTURE);
+ }
+ }
+ else {
+ if (bgnd.getSkyColor().length > 1 || bgnd.getGroundColor().length) {
+ sphere = new x3dom.nodeTypes.Sphere();
+ texture = gl.createTexture();
+
+ bgnd._webgl = {
+ positions: sphere._mesh._positions[0],
+ texcoords: sphere._mesh._texCoords[0],
+ indexes: sphere._mesh._indices[0],
+ buffers: [
+ {}, {}, {}
+ ],
+ texture: texture,
+ primType: gl.TRIANGLES
+ };
+
+ var N = x3dom.Utils.nextHighestPowerOfTwo(
+ bgnd.getSkyColor().length + bgnd.getGroundColor().length + 2);
+ N = (N < 512) ? 512 : N;
+
+ var n = bgnd._vf.groundAngle.length;
+ var tmp = [], arr = [];
+ var colors = [], sky = [0];
+
+ for (i = 0; i < bgnd._vf.skyColor.length; i++) {
+ colors[i] = bgnd._vf.skyColor[i];
+ }
+
+ for (i = 0; i < bgnd._vf.skyAngle.length; i++) {
+ sky[i + 1] = bgnd._vf.skyAngle[i];
+ }
+
+ if (n > 0 || bgnd._vf.groundColor.length == 1) {
+ if (sky[sky.length - 1] < Math.PI / 2) {
+ sky[sky.length] = Math.PI / 2 - x3dom.fields.Eps;
+ colors[colors.length] = colors[colors.length - 1];
+ }
+
+ for (i = n - 1; i >= 0; i--) {
+ if ((i == n - 1) && (Math.PI - bgnd._vf.groundAngle[i] <= Math.PI / 2)) {
+ sky[sky.length] = Math.PI / 2;
+ colors[colors.length] = bgnd._vf.groundColor[bgnd._vf.groundColor.length - 1];
+ }
+ sky[sky.length] = Math.PI - bgnd._vf.groundAngle[i];
+ colors[colors.length] = bgnd._vf.groundColor[i + 1];
+ }
+
+ if (n == 0 && bgnd._vf.groundColor.length == 1) {
+ sky[sky.length] = Math.PI / 2;
+ colors[colors.length] = bgnd._vf.groundColor[0];
+ }
+ sky[sky.length] = Math.PI;
+ colors[colors.length] = bgnd._vf.groundColor[0];
+ }
+ else {
+ if (sky[sky.length - 1] < Math.PI) {
+ sky[sky.length] = Math.PI;
+ colors[colors.length] = colors[colors.length - 1];
+ }
+ }
+
+ for (i = 0; i < sky.length; i++) {
+ sky[i] /= Math.PI;
+ }
+
+ if (sky.length != colors.length) {
+ x3dom.debug.logError("Number of background colors and corresponding angles are different!");
+ var minArrayLength = (sky.length < colors.length) ? sky.length : colors.length;
+ sky.length = minArrayLength;
+ colors.length = minArrayLength;
+ }
+
+ var interp = new x3dom.nodeTypes.ColorInterpolator();
+
+ interp._vf.key = new x3dom.fields.MFFloat(sky);
+ interp._vf.keyValue = new x3dom.fields.MFColor(colors);
+
+ for (i = 0; i < N; i++) {
+ interp._vf.set_fraction = i / (N - 1.0);
+
+ interp.fieldChanged("set_fraction");
+ tmp[i] = interp._vf.value_changed;
+ }
+
+ tmp.reverse();
+
+ var alpha = Math.floor((1.0 - bgnd.getTransparency()) * 255);
+
+ for (i = 0; i < tmp.length; i++) {
+ arr.push(Math.floor(tmp[i].r * 255),
+ Math.floor(tmp[i].g * 255),
+ Math.floor(tmp[i].b * 255),
+ alpha);
+ }
+
+ var pixels = new Uint8Array(arr);
+ var format = gl.RGBA;
+
+ N = pixels.length / 4;
+
+ gl.bindTexture(gl.TEXTURE_2D, texture);
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
+ 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.pixelStorei(gl.UNPACK_ALIGNMENT, 1);
+ gl.texImage2D(gl.TEXTURE_2D, 0, format, 1, N, 0, format, gl.UNSIGNED_BYTE, pixels);
+ gl.bindTexture(gl.TEXTURE_2D, null);
+
+ bgnd._webgl.shader = this.cache.getShader(gl, x3dom.shader.BACKGROUND_SKYTEXTURE);
+ }
+ else {
+ // Impl. gradient bg etc., e.g. via canvas 2d? But can be done via CSS anyway...
+ bgnd._webgl = {};
+ }
+ }
+
+ if (bgnd._webgl.shader) {
+ var sp = bgnd._webgl.shader;
+
+ var positionBuffer = gl.createBuffer();
+ bgnd._webgl.buffers[1] = positionBuffer;
+ gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
+
+ var vertices = new Float32Array(bgnd._webgl.positions);
+
+ gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);
+ gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
+
+ gl.vertexAttribPointer(sp.position, 3, gl.FLOAT, false, 0, 0);
+ gl.enableVertexAttribArray(sp.position);
+
+ var indicesBuffer = gl.createBuffer();
+ bgnd._webgl.buffers[0] = indicesBuffer;
+
+ var indexArray = new Uint16Array(bgnd._webgl.indexes);
+
+ gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indicesBuffer);
+ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indexArray, gl.STATIC_DRAW);
+
+ vertices = null;
+ indexArray = null;
+
+ if (sp.texcoord !== undefined) {
+ var texcBuffer = gl.createBuffer();
+ bgnd._webgl.buffers[2] = texcBuffer;
+
+ var texcoords = new Float32Array(bgnd._webgl.texcoords);
+
+ gl.bindBuffer(gl.ARRAY_BUFFER, texcBuffer);
+ gl.bufferData(gl.ARRAY_BUFFER, texcoords, gl.STATIC_DRAW);
+
+ gl.vertexAttribPointer(sp.texcoord, 2, gl.FLOAT, false, 0, 0);
+ gl.enableVertexAttribArray(sp.texcoord);
+
+ texcoords = null;
+ }
+
+ bgnd._cleanupGLObjects = function () {
+ var sp = this._webgl.shader;
+
+ if (sp.position !== undefined) {
+ gl.deleteBuffer(this._webgl.buffers[0]);
+ gl.deleteBuffer(this._webgl.buffers[1]);
+ }
+ if (sp.texcoord !== undefined) {
+ gl.deleteBuffer(this._webgl.buffers[2]);
+ }
+ };
+ }
+
+ bgnd._webgl.render = function (gl, mat_view, mat_proj)
+ {
+ var sp = bgnd._webgl.shader;
+ var alpha = 1.0 - bgnd.getTransparency();
+
+ var mat_scene = null;
+ var projMatrix_22 = mat_proj._22,
+ projMatrix_23 = mat_proj._23;
+ var camPos = mat_view.e3();
+
+ if ((sp !== undefined && sp !== null) &&
+ (sp.texcoord !== undefined && sp.texcoord !== null) &&
+ (bgnd._webgl.texture !== undefined && bgnd._webgl.texture !== null)) {
+ gl.clearColor(0, 0, 0, alpha);
+ gl.clearDepth(1.0);
+ gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT | gl.STENCIL_BUFFER_BIT);
+
+ that.stateManager.frontFace(gl.CCW);
+ that.stateManager.disable(gl.CULL_FACE);
+ that.stateManager.disable(gl.DEPTH_TEST);
+ that.stateManager.disable(gl.BLEND);
+
+ that.stateManager.useProgram(sp);
+
+ if (!sp.tex) {
+ sp.tex = 0;
+ }
+
+ // adapt projection matrix to better near/far
+ mat_proj._22 = 100001 / 99999;
+ mat_proj._23 = 200000 / 99999;
+ // center viewpoint
+ mat_view._03 = 0;
+ mat_view._13 = 0;
+ mat_view._23 = 0;
+
+ mat_scene = mat_proj.mult(mat_view);
+ sp.modelViewProjectionMatrix = mat_scene.toGL();
+
+ mat_view._03 = camPos.x;
+ mat_view._13 = camPos.y;
+ mat_view._23 = camPos.z;
+
+ mat_proj._22 = projMatrix_22;
+ mat_proj._23 = projMatrix_23;
+
+ gl.activeTexture(gl.TEXTURE0);
+ gl.bindTexture(gl.TEXTURE_2D, bgnd._webgl.texture);
+
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
+ 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.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, bgnd._webgl.buffers[0]);
+
+ gl.bindBuffer(gl.ARRAY_BUFFER, bgnd._webgl.buffers[1]);
+ gl.vertexAttribPointer(sp.position, 3, gl.FLOAT, false, 0, 0);
+ gl.enableVertexAttribArray(sp.position);
+
+ gl.bindBuffer(gl.ARRAY_BUFFER, bgnd._webgl.buffers[2]);
+ gl.vertexAttribPointer(sp.texcoord, 2, gl.FLOAT, false, 0, 0);
+ gl.enableVertexAttribArray(sp.texcoord);
+
+ gl.drawElements(bgnd._webgl.primType, bgnd._webgl.indexes.length, gl.UNSIGNED_SHORT, 0);
+
+ gl.activeTexture(gl.TEXTURE0);
+ gl.bindTexture(gl.TEXTURE_2D, null);
+
+ gl.disableVertexAttribArray(sp.position);
+ gl.disableVertexAttribArray(sp.texcoord);
+
+ gl.clear(gl.DEPTH_BUFFER_BIT);
+ }
+ else if (!sp || !bgnd._webgl.texture ||
+ (bgnd._webgl.texture.textureCubeReady !== undefined &&
+ bgnd._webgl.texture.textureCubeReady !== true)) {
+ var bgCol = bgnd.getSkyColor().toGL();
+
+ gl.clearColor(bgCol[0], bgCol[1], bgCol[2], alpha);
+ gl.clearDepth(1.0);
+ gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT | gl.STENCIL_BUFFER_BIT);
+ }
+ else {
+ gl.clearColor(0, 0, 0, alpha);
+ gl.clearDepth(1.0);
+ gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT | gl.STENCIL_BUFFER_BIT);
+
+ that.stateManager.frontFace(gl.CCW);
+ that.stateManager.disable(gl.CULL_FACE);
+ that.stateManager.disable(gl.DEPTH_TEST);
+ that.stateManager.disable(gl.BLEND);
+
+ that.stateManager.useProgram(sp);
+
+ if (!sp.tex) {
+ sp.tex = 0;
+ }
+
+ if (bgnd._webgl.texture.textureCubeReady) {
+ // adapt projection matrix to better near/far
+ mat_proj._22 = 100001 / 99999;
+ mat_proj._23 = 200000 / 99999;
+ // center viewpoint
+ mat_view._03 = 0;
+ mat_view._13 = 0;
+ mat_view._23 = 0;
+
+ mat_scene = mat_proj.mult(mat_view);
+ sp.modelViewProjectionMatrix = mat_scene.toGL();
+
+ mat_view._03 = camPos.x;
+ mat_view._13 = camPos.y;
+ mat_view._23 = camPos.z;
+
+ mat_proj._22 = projMatrix_22;
+ mat_proj._23 = projMatrix_23;
+
+ gl.activeTexture(gl.TEXTURE0);
+ gl.bindTexture(gl.TEXTURE_CUBE_MAP, bgnd._webgl.texture);
+
+ gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
+ gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
+ gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
+ gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
+ }
+ else {
+ gl.activeTexture(gl.TEXTURE0);
+ gl.bindTexture(gl.TEXTURE_2D, bgnd._webgl.texture);
+
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
+ 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.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, bgnd._webgl.buffers[0]);
+ gl.bindBuffer(gl.ARRAY_BUFFER, bgnd._webgl.buffers[1]);
+ gl.vertexAttribPointer(sp.position, 3, gl.FLOAT, false, 0, 0);
+ gl.enableVertexAttribArray(sp.position);
+
+ gl.drawElements(bgnd._webgl.primType, bgnd._webgl.indexes.length, gl.UNSIGNED_SHORT, 0);
+
+ gl.disableVertexAttribArray(sp.position);
+
+ gl.activeTexture(gl.TEXTURE0);
+ if (bgnd._webgl.texture.textureCubeReady) {
+ gl.bindTexture(gl.TEXTURE_CUBE_MAP, null);
+ }
+ else {
+ gl.bindTexture(gl.TEXTURE_2D, null);
+ }
+
+ gl.clear(gl.DEPTH_BUFFER_BIT);
+ }
+ };
+ };
+
+
+ /*****************************************************************************
+ * Setup Frontgrounds
+ *****************************************************************************/
+ Context.prototype.setupFgnds = function (gl, scene) {
+ if (scene._fgnd !== undefined) {
+ return;
+ }
+
+ var that = this;
+
+ var w = 1, h = 1;
+ scene._fgnd = {};
+
+ scene._fgnd._webgl = {
+ positions: [-w, -h, 0, -w, h, 0, w, -h, 0, w, h, 0],
+ indexes: [0, 1, 2, 3],
+ buffers: [
+ {}, {}
+ ]
+ };
+
+ scene._fgnd._webgl.primType = gl.TRIANGLE_STRIP;
+
+ scene._fgnd._webgl.shader = this.cache.getShader(gl, x3dom.shader.FRONTGROUND_TEXTURE);
+
+ var sp = scene._fgnd._webgl.shader;
+
+ var positionBuffer = gl.createBuffer();
+ scene._fgnd._webgl.buffers[1] = positionBuffer;
+ gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
+
+ var vertices = new Float32Array(scene._fgnd._webgl.positions);
+
+ gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);
+ gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
+
+ gl.vertexAttribPointer(sp.position, 3, gl.FLOAT, false, 0, 0);
+
+ var indicesBuffer = gl.createBuffer();
+ scene._fgnd._webgl.buffers[0] = indicesBuffer;
+
+ var indexArray = new Uint16Array(scene._fgnd._webgl.indexes);
+
+ gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indicesBuffer);
+ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indexArray, gl.STATIC_DRAW);
+
+ vertices = null;
+ indexArray = null;
+
+ scene._fgnd._webgl.render = function (gl, tex) {
+ scene._fgnd._webgl.texture = tex;
+
+ that.stateManager.frontFace(gl.CCW);
+ that.stateManager.disable(gl.CULL_FACE);
+ that.stateManager.disable(gl.DEPTH_TEST);
+
+ that.stateManager.useProgram(sp);
+
+ if (!sp.tex) {
+ sp.tex = 0;
+ }
+
+ //this.stateManager.enable(gl.TEXTURE_2D);
+ gl.activeTexture(gl.TEXTURE0);
+ gl.bindTexture(gl.TEXTURE_2D, scene._fgnd._webgl.texture);
+
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
+ 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.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, scene._fgnd._webgl.buffers[0]);
+ gl.bindBuffer(gl.ARRAY_BUFFER, scene._fgnd._webgl.buffers[1]);
+ gl.vertexAttribPointer(sp.position, 3, gl.FLOAT, false, 0, 0);
+ gl.enableVertexAttribArray(sp.position);
+
+ gl.drawElements(scene._fgnd._webgl.primType, scene._fgnd._webgl.indexes.length, gl.UNSIGNED_SHORT, 0);
+
+ gl.disableVertexAttribArray(sp.position);
+
+ gl.activeTexture(gl.TEXTURE0);
+ gl.bindTexture(gl.TEXTURE_2D, null);
+ //this.stateManager.disable(gl.TEXTURE_2D);
+ };
+ };
+
+
+ /*****************************************************************************
+ * Render Shadow-Pass
+ *****************************************************************************/
+ Context.prototype.renderShadowPass = function (gl, viewarea, mat_scene, mat_view, targetFbo, camOffset, isCameraView)
+ {
+ var scene = viewarea._scene;
+ var sp = scene._webgl.shadowShader;
+
+ this.stateManager.bindFramebuffer(gl.FRAMEBUFFER, targetFbo.fbo);
+ this.stateManager.viewport(0, 0, targetFbo.width, targetFbo.height);
+
+ this.stateManager.useProgram(sp);
+
+ sp.cameraView = isCameraView;
+ sp.offset = camOffset;
+
+ // workaround for old graphics cards/ drivers
+ {
+ sp.PG_precisionLevel = 1.0;
+ sp.PG_powPrecision = 1.0;
+ sp.PG_maxBBSize = [0, 0, 0];
+ sp.PG_bbMin = [0, 0, 0];
+ sp.PG_bbMaxModF = [0, 0, 0];
+ sp.PG_bboxShiftVec = [0, 0, 0];
+ }
+
+ gl.clearColor(1.0, 1.0, 1.0, 0.0);
+ gl.clearDepth(1.0);
+ gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
+
+ this.stateManager.depthFunc(gl.LEQUAL);
+ this.stateManager.enable(gl.DEPTH_TEST);
+ this.stateManager.enable(gl.CULL_FACE);
+ this.stateManager.disable(gl.BLEND);
+
+ var bgCenter = x3dom.fields.SFVec3f.NullVector.toGL();
+ var bgSize = x3dom.fields.SFVec3f.OneVector.toGL();
+
+ var env = scene.getEnvironment();
+ var excludeTrans = env._vf.shadowExcludeTransparentObjects;
+
+ var i, n = scene.drawableCollection.length;
+
+ for (i = 0; i < n; i++)
+ {
+ var drawable = scene.drawableCollection.get(i);
+ var trafo = drawable.transform;
+ var shape = drawable.shape;
+
+ var s_gl = shape._webgl;
+
+ if (!s_gl || (excludeTrans && drawable.sortType == 'transparent')) {
+ continue;
+ }
+
+ var s_geo = shape._cf.geometry.node;
+ var s_msh = s_geo._mesh;
+
+ sp.modelViewProjectionMatrix = mat_scene.mult(trafo).toGL();
+
+ //Set ImageGeometry switch
+ sp.imageGeometry = s_gl.imageGeometry;
+ sp.popGeometry = s_gl.popGeometry;
+
+ if (s_gl.coordType != gl.FLOAT) {
+ if (!s_gl.popGeometry && (x3dom.Utils.isUnsignedType(s_geo._vf.coordType))) {
+ sp.bgCenter = s_geo.getMin().toGL();
+ }
+ else {
+ sp.bgCenter = s_geo._vf.position.toGL();
+ }
+ sp.bgSize = s_geo._vf.size.toGL();
+ sp.bgPrecisionMax = s_geo.getPrecisionMax('coordType');
+ }
+ else {
+ sp.bgCenter = bgCenter;
+ sp.bgSize = bgSize;
+ sp.bgPrecisionMax = 1;
+ }
+
+ if (s_gl.colorType != gl.FLOAT) {
+ sp.bgPrecisionColMax = s_geo.getPrecisionMax('colorType');
+ }
+
+ if (s_gl.texCoordType != gl.FLOAT) {
+ sp.bgPrecisionTexMax = s_geo.getPrecisionMax('texCoordType');
+ }
+
+ if (s_gl.imageGeometry != 0 && !x3dom.caps.MOBILE) // FIXME: mobile errors
+ {
+ sp.IG_bboxMin = s_geo.getMin().toGL();
+ sp.IG_bboxMax = s_geo.getMax().toGL();
+ sp.IG_implicitMeshSize = s_geo._vf.implicitMeshSize.toGL(); // FIXME
+
+ var coordTex = x3dom.Utils.findTextureByName(s_gl.texture, "IG_coords0");
+ if (coordTex) {
+ sp.IG_coordTextureWidth = coordTex.texture.width;
+ sp.IG_coordTextureHeight = coordTex.texture.height;
+ }
+
+ if (s_gl.imageGeometry == 1) {
+ var indexTex = x3dom.Utils.findTextureByName(s_gl.texture, "IG_index");
+ if (indexTex) {
+ sp.IG_indexTextureWidth = indexTex.texture.width;
+ sp.IG_indexTextureHeight = indexTex.texture.height;
+ }
+
+ gl.activeTexture(gl.TEXTURE0);
+ gl.bindTexture(gl.TEXTURE_2D, indexTex.texture);
+
+ gl.activeTexture(gl.TEXTURE1);
+ gl.bindTexture(gl.TEXTURE_2D, coordTex.texture);
+ }
+ else {
+ gl.activeTexture(gl.TEXTURE0);
+ gl.bindTexture(gl.TEXTURE_2D, coordTex.texture);
+ }
+
+ 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);
+
+ var texUnit = 0;
+ if (s_geo.getIndexTexture()) {
+ if (!sp.IG_indexTexture) {
+ sp.IG_indexTexture = texUnit++;
+ }
+ }
+ if (s_geo.getCoordinateTexture(0)) {
+ if (!sp.IG_coordinateTexture) {
+ sp.IG_coordinateTexture = texUnit++;
+ }
+ }
+ }
+
+ if (shape.isSolid()) {
+ this.stateManager.enable(gl.CULL_FACE);
+
+ if (shape.isCCW()) {
+ this.stateManager.frontFace(gl.CCW);
+ }
+ else {
+ this.stateManager.frontFace(gl.CW);
+ }
+ }
+ else {
+ this.stateManager.disable(gl.CULL_FACE);
+ }
+
+ //PopGeometry: adapt LOD and set shader variables
+ if (s_gl.popGeometry) {
+ var model_view = mat_view.mult(trafo);
+ this.updatePopState(drawable, s_geo, sp, s_gl, scene, model_view, viewarea, this.x3dElem.runtime.fps);
+ }
+
+ var q_n;
+ if (s_gl.externalGeometry != 0)
+ {
+ q_n = s_gl.primType.length;
+ }
+ else
+ {
+ q_n = s_gl.positions.length;
+ }
+ for (var q = 0; q < q_n; q++) {
+ var q6 = 6 * q;
+ var v, v_n, offset;
+
+ if ( !(sp.position !== undefined && s_gl.buffers[q6 + 1] && (s_gl.indexes[q] || s_gl.externalGeometry != 0)) )
+ continue;
+
+ // set buffers
+ if (s_gl.buffers[q6]) {
+ gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, s_gl.buffers[q6]);
+ }
+
+ gl.bindBuffer(gl.ARRAY_BUFFER, s_gl.buffers[q6 + 1]);
+
+ gl.vertexAttribPointer(sp.position,
+ s_msh._numPosComponents, s_gl.coordType, false,
+ shape._coordStrideOffset[0], shape._coordStrideOffset[1]);
+ gl.enableVertexAttribArray(sp.position);
+
+ if (s_gl.binaryGeometry > 0 || s_gl.popGeometry > 0) {
+ for (v = 0, offset = 0, v_n = s_geo._vf.vertexCount.length; v < v_n; v++) {
+ gl.drawElements(s_gl.primType[v], s_geo._vf.vertexCount[v], s_gl.indexType,
+ x3dom.Utils.getByteAwareOffset(offset, s_gl.indexType, gl));
+ offset += s_geo._vf.vertexCount[v];
+ }
+ }
+ else if (s_gl.binaryGeometry < 0 || s_gl.popGeometry < 0 || s_gl.imageGeometry) {
+ for (v = 0, offset = 0, v_n = s_geo._vf.vertexCount.length; v < v_n; v++) {
+ gl.drawArrays(s_gl.primType[v], offset, s_geo._vf.vertexCount[v]);
+ offset += s_geo._vf.vertexCount[v];
+ }
+ }
+ //ExternalGeometry: indexed rendering (shadow pass)
+ else if (s_gl.externalGeometry == 1)
+ {
+ gl.drawElements(s_gl.primType[q], s_gl.drawCount[q], s_gl.indexType, s_gl.indexOffset[q]);
+ }
+ //ExternalGeometry: non-indexed rendering (shadow pass)
+ else if (s_gl.externalGeometry == -1)
+ {
+ gl.drawArrays(s_gl.primType[q], 0, s_gl.drawCount[q]);
+ }
+ else if (s_geo.hasIndexOffset()) {
+ var indOff = shape.tessellationProperties();
+ for (v = 0, v_n = indOff.length; v < v_n; v++) {
+ gl.drawElements(s_gl.primType, indOff[v].count, s_gl.indexType,
+ indOff[v].offset * x3dom.Utils.getOffsetMultiplier(s_gl.indexType, gl));
+ }
+ }
+ else if (s_gl.indexes[q].length == 0) {
+ gl.drawArrays(s_gl.primType, 0, s_gl.positions[q].length / 3);
+ }
+ else {
+ gl.drawElements(s_gl.primType, s_gl.indexes[q].length, s_gl.indexType, 0);
+ }
+
+ gl.disableVertexAttribArray(sp.position);
+ }
+
+ //Clean Texture units for IG
+ if (s_gl.imageGeometry != 0 && !x3dom.caps.MOBILE) {
+ gl.activeTexture(gl.TEXTURE0);
+ gl.bindTexture(gl.TEXTURE_2D, null);
+ if (s_gl.imageGeometry == 1) {
+ gl.activeTexture(gl.TEXTURE1);
+ gl.bindTexture(gl.TEXTURE_2D, null);
+ }
+ }
+ }
+
+ gl.flush();
+ this.stateManager.bindFramebuffer(gl.FRAMEBUFFER, null);
+ };
+
+ /*****************************************************************************
+ * Render Picking-Pass
+ *****************************************************************************/
+ Context.prototype.renderPickingPass = function (gl, scene, mat_view, mat_scene, from, sceneSize,
+ pickMode, lastX, lastY, width, height)
+ {
+ var ps = scene._webgl.pickScale;
+ var bufHeight = scene._webgl.fboPick.height;
+ var x = lastX * ps;
+ var y = (bufHeight - 1) - lastY * ps;
+
+ this.stateManager.bindFramebuffer(gl.FRAMEBUFFER, scene._webgl.fboPick.fbo);
+ this.stateManager.viewport(0, 0, scene._webgl.fboPick.width, bufHeight);
+
+ //gl.scissor(x, y, width, height);
+ //gl.enable(gl.SCISSOR_TEST);
+
+ gl.clearColor(0.0, 0.0, 0.0, 0.0);
+ gl.clearDepth(1.0);
+ gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
+
+ var viewarea = scene.drawableCollection.viewarea;
+ var env = scene.getEnvironment();
+ var n = scene.drawableCollection.length;
+
+ if (env._vf.smallFeatureCulling && env._lowPriorityThreshold < 1 && viewarea.isMovingOrAnimating()) {
+ n = Math.floor(n * env._lowPriorityThreshold);
+ if (!n && scene.drawableCollection.length)
+ n = 1; // render at least one object
+ }
+
+ var bgCenter = x3dom.fields.SFVec3f.NullVector.toGL();
+ var bgSize = x3dom.fields.SFVec3f.OneVector.toGL();
+
+ this.stateManager.depthFunc(gl.LEQUAL);
+ this.stateManager.enable(gl.DEPTH_TEST);
+ this.stateManager.enable(gl.CULL_FACE);
+ this.stateManager.disable(gl.BLEND);
+
+ if (x3dom.Utils.needLineWidth) {
+ this.stateManager.lineWidth(2); // bigger lines for better picking
+ }
+
+ for (var i = 0; i < n; i++)
+ {
+ var drawable = scene.drawableCollection.get(i);
+ var trafo = drawable.transform;
+ var shape = drawable.shape;
+ var s_gl = shape._webgl;
+
+ if (!s_gl || shape._objectID < 1 || !shape._vf.isPickable) {
+ continue;
+ }
+
+ var s_geo = shape._cf.geometry.node;
+ var s_app = shape._cf.appearance.node;
+ var s_msh = s_geo._mesh;
+
+ //Get shapes shader properties
+ var properties = shape.getShaderProperties(viewarea);
+
+ //Generate Dynamic picking shader
+ var sp = this.cache.getShaderByProperties(gl, shape, properties, pickMode);
+
+ if (!sp) { // error
+ return;
+ }
+
+ //Bind shader
+ this.stateManager.useProgram(sp);
+
+ sp.modelMatrix = trafo.toGL();
+ sp.modelViewProjectionMatrix = mat_scene.mult(trafo).toGL();
+
+ sp.lowBit = (shape._objectID & 255) / 255.0;
+ sp.highBit = (shape._objectID >>> 8) / 255.0;
+
+ sp.from = from.toGL();
+ sp.sceneSize = sceneSize;
+
+ // Set shadow ids if available
+ if(s_gl.binaryGeometry != 0 && s_geo._vf.idsPerVertex) {
+ sp.shadowIDs = (shape._vf.idOffset + x3dom.nodeTypes.Shape.objectID + 2);
+ }
+
+ // BoundingBox stuff
+ if (s_gl.coordType != gl.FLOAT) {
+ if (!s_gl.popGeometry && (x3dom.Utils.isUnsignedType(s_geo._vf.coordType))) {
+ sp.bgCenter = s_geo.getMin().toGL();
+ }
+ else {
+ sp.bgCenter = s_geo._vf.position.toGL();
+ }
+ sp.bgSize = s_geo._vf.size.toGL();
+ sp.bgPrecisionMax = s_geo.getPrecisionMax('coordType');
+ }
+
+ if (pickMode == 1 && s_gl.colorType != gl.FLOAT) {
+ sp.bgPrecisionColMax = s_geo.getPrecisionMax('colorType');
+ }
+
+ if (pickMode == 2 && s_gl.texCoordType != gl.FLOAT) {
+ sp.bgPrecisionTexMax = s_geo.getPrecisionMax('texCoordType');
+ }
+
+ //===========================================================================
+ // Set ClipPlanes
+ //===========================================================================
+ if (shape._clipPlanes) {
+ sp.modelViewMatrix = mat_view.mult(trafo).toGL();
+ sp.viewMatrixInverse = mat_view.inverse().toGL();
+ for (var cp = 0; cp < shape._clipPlanes.length; cp++) {
+ var clip_plane = shape._clipPlanes[cp].plane;
+ var clip_trafo = shape._clipPlanes[cp].trafo;
+
+ sp['clipPlane' + cp + '_Plane'] = clip_trafo.multMatrixPlane(clip_plane._vf.plane).toGL();
+ sp['clipPlane' + cp + '_CappingStrength'] = clip_plane._vf.cappingStrength;
+ sp['clipPlane' + cp + '_CappingColor'] = clip_plane._vf.cappingColor.toGL();
+ }
+ }
+
+ //ImageGeometry stuff
+ if (s_gl.imageGeometry != 0 && !x3dom.caps.MOBILE) // FIXME: mobile errors
+ {
+ sp.IG_bboxMin = s_geo.getMin().toGL();
+ sp.IG_bboxMax = s_geo.getMax().toGL();
+ sp.IG_implicitMeshSize = s_geo._vf.implicitMeshSize.toGL(); // FIXME
+
+ var coordTex = x3dom.Utils.findTextureByName(s_gl.texture, "IG_coords0");
+ if (coordTex) {
+ sp.IG_coordTextureWidth = coordTex.texture.width;
+ sp.IG_coordTextureHeight = coordTex.texture.height;
+ }
+
+ if (s_gl.imageGeometry == 1) {
+ var indexTex = x3dom.Utils.findTextureByName(s_gl.texture, "IG_index");
+ if (indexTex) {
+ sp.IG_indexTextureWidth = indexTex.texture.width;
+ sp.IG_indexTextureHeight = indexTex.texture.height;
+ }
+
+ gl.activeTexture(gl.TEXTURE0);
+ gl.bindTexture(gl.TEXTURE_2D, indexTex.texture);
+
+ gl.activeTexture(gl.TEXTURE1);
+ gl.bindTexture(gl.TEXTURE_2D, coordTex.texture);
+ }
+ else {
+ gl.activeTexture(gl.TEXTURE0);
+ gl.bindTexture(gl.TEXTURE_2D, coordTex.texture);
+ }
+
+ 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);
+
+ var texUnit = 0;
+ if (s_geo.getIndexTexture()) {
+ if (!sp.IG_indexTexture) {
+ sp.IG_indexTexture = texUnit++;
+ }
+ }
+ if (s_geo.getCoordinateTexture(0)) {
+ if (!sp.IG_coordinateTexture) {
+ sp.IG_coordinateTexture = texUnit++;
+ }
+ }
+ }
+ else if (s_gl.binaryGeometry != 0 && s_geo._vf.idsPerVertex) { //MultiPart
+ var shader = s_app._shader;
+ if(shader && x3dom.isa(s_app._shader, x3dom.nodeTypes.CommonSurfaceShader)) {
+ if (shader.getMultiVisibilityMap()) {
+ sp.multiVisibilityMap = 0;
+ var visTex = x3dom.Utils.findTextureByName(s_gl.texture, "multiVisibilityMap");
+ sp.multiVisibilityWidth = visTex.texture.width;
+ sp.multiVisibilityHeight = visTex.texture.height;
+ gl.activeTexture(gl.TEXTURE0);
+ gl.bindTexture(gl.TEXTURE_2D, visTex.texture);
+ 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);
+ }
+ }
+ }
+
+ if (shape.isSolid()) {
+ this.stateManager.enable(gl.CULL_FACE);
+
+ if (shape.isCCW()) {
+ this.stateManager.frontFace(gl.CCW);
+ }
+ else {
+ this.stateManager.frontFace(gl.CW);
+ }
+ }
+ else {
+ this.stateManager.disable(gl.CULL_FACE);
+ }
+
+
+ //===========================================================================
+ // Set DepthMode
+ //===========================================================================
+ var depthMode = s_app ? s_app._cf.depthMode.node : null;
+ if (depthMode)
+ {
+ if (depthMode._vf.enableDepthTest)
+ {
+ //Enable Depth Test
+ this.stateManager.enable(gl.DEPTH_TEST);
+
+ //Set Depth Mask
+ this.stateManager.depthMask(!depthMode._vf.readOnly);
+
+ //Set Depth Function
+ this.stateManager.depthFunc(x3dom.Utils.depthFunc(gl, depthMode._vf.depthFunc));
+
+ //Set Depth Range
+ this.stateManager.depthRange(depthMode._vf.zNearRange, depthMode._vf.zFarRange);
+ }
+ else
+ {
+ //Disable Depth Test
+ this.stateManager.disable(gl.DEPTH_TEST);
+ }
+ }
+ else //Set Defaults
+ {
+ this.stateManager.enable(gl.DEPTH_TEST);
+ this.stateManager.depthMask(true);
+ this.stateManager.depthFunc(gl.LEQUAL);
+ }
+
+ //PopGeometry: adapt LOD and set shader variables
+ if (s_gl.popGeometry) {
+ var model_view = mat_view.mult(trafo);
+ // FIXME; viewarea's width/height twice as big as render buffer size, which leads to too high precision
+ // the correct viewarea here would be one that holds this half-sized render buffer
+ this.updatePopState(drawable, s_geo, sp, s_gl, scene, model_view, viewarea, this.x3dElem.runtime.fps);
+ }
+
+ var q_n;
+ if (s_gl.externalGeometry != 0)
+ {
+ q_n = s_gl.primType.length;
+ }
+ else
+ {
+ q_n = s_gl.positions.length;
+ }
+ for (var q = 0; q < q_n; q++) {
+ var q6 = 6 * q;
+ var v, v_n, offset;
+
+ if ( !(sp.position !== undefined && s_gl.buffers[q6 + 1] && (s_gl.indexes[q] || s_gl.externalGeometry != 0)) )
+ continue;
+
+ // set buffers
+ if (s_gl.buffers[q6]) {
+ gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, s_gl.buffers[q6]);
+ }
+
+ gl.bindBuffer(gl.ARRAY_BUFFER, s_gl.buffers[q6 + 1]);
+
+ gl.vertexAttribPointer(sp.position,
+ s_msh._numPosComponents, s_gl.coordType, false,
+ shape._coordStrideOffset[0], shape._coordStrideOffset[1]);
+ gl.enableVertexAttribArray(sp.position);
+
+ if (pickMode == 1 && sp.color !== undefined && s_gl.buffers[q6 + 4]) {
+ gl.bindBuffer(gl.ARRAY_BUFFER, s_gl.buffers[q6 + 4]);
+
+ gl.vertexAttribPointer(sp.color,
+ s_msh._numColComponents, s_gl.colorType, false,
+ shape._colorStrideOffset[0], shape._colorStrideOffset[1]);
+ gl.enableVertexAttribArray(sp.color);
+ }
+
+ if (pickMode == 2 && sp.texcoord !== undefined && s_gl.buffers[q6 + 3]) {
+ gl.bindBuffer(gl.ARRAY_BUFFER, s_gl.buffers[q6 + 3]);
+
+ gl.vertexAttribPointer(sp.texcoord,
+ s_msh._numTexComponents, s_gl.texCoordType, false,
+ shape._texCoordStrideOffset[0], shape._texCoordStrideOffset[1]);
+ gl.enableVertexAttribArray(sp.texcoord);
+ }
+
+ if (sp.id !== undefined && s_gl.buffers[q6 + 5]) {
+
+ gl.bindBuffer(gl.ARRAY_BUFFER, s_gl.buffers[q6 + 5]);
+ //texture coordinate hack for IDs
+ if (s_gl.binaryGeometry != 0 && s_geo._vf["idsPerVertex"] == true)
+ {
+ gl.vertexAttribPointer(sp.id,
+ 1, gl.FLOAT, false,
+ 4, 0);
+ gl.enableVertexAttribArray(sp.id);
+ }
+ else
+ {
+ /*
+ gl.vertexAttribPointer(sp.id,
+ 1, gl.FLOAT, false,
+ shape._idStrideOffset[0], shape._idStrideOffset[1]);
+ gl.enableVertexAttribArray(sp.id);
+ */
+ }
+ }
+
+ // render mesh
+ if (s_gl.binaryGeometry > 0 || s_gl.popGeometry > 0) {
+ for (v = 0, offset = 0, v_n = s_geo._vf.vertexCount.length; v < v_n; v++) {
+ gl.drawElements(s_gl.primType[v], s_geo._vf.vertexCount[v], s_gl.indexType,
+ x3dom.Utils.getByteAwareOffset(offset, s_gl.indexType, gl));
+ offset += s_geo._vf.vertexCount[v];
+ }
+ }
+ else if (s_gl.binaryGeometry < 0 || s_gl.popGeometry < 0 || s_gl.imageGeometry) {
+ for (v = 0, offset = 0, v_n = s_geo._vf.vertexCount.length; v < v_n; v++) {
+ gl.drawArrays(s_gl.primType[v], offset, s_geo._vf.vertexCount[v]);
+ offset += s_geo._vf.vertexCount[v];
+ }
+ }
+ //ExternalGeometry: indexed rendering (picking pass)
+ else if (s_gl.externalGeometry == 1)
+ {
+ gl.drawElements(s_gl.primType[q], s_gl.drawCount[q], s_gl.indexType, s_gl.indexOffset[q]);
+ }
+ //ExternalGeometry: non-indexed rendering (picking pass)
+ else if (s_gl.externalGeometry == -1)
+ {
+ gl.drawArrays(s_gl.primType[q], 0, s_gl.drawCount[q]);
+ }
+ else if (s_geo.hasIndexOffset()) {
+ var indOff = shape.tessellationProperties();
+ for (v = 0, v_n = indOff.length; v < v_n; v++) {
+ gl.drawElements(s_gl.primType, indOff[v].count, s_gl.indexType,
+ indOff[v].offset * x3dom.Utils.getOffsetMultiplier(s_gl.indexType, gl));
+ }
+ }
+ else if (s_gl.indexes[q].length == 0) {
+ gl.drawArrays(s_gl.primType, 0, s_gl.positions[q].length / 3);
+ }
+ else {
+ gl.drawElements(s_gl.primType, s_gl.indexes[q].length, s_gl.indexType, 0);
+ }
+
+ gl.disableVertexAttribArray(sp.position);
+
+ if (sp.texcoord !== undefined && s_gl.buffers[q6 + 3]) {
+ gl.disableVertexAttribArray(sp.texcoord);
+ }
+ if (sp.color !== undefined && s_gl.buffers[q6 + 4]) {
+ gl.disableVertexAttribArray(sp.color);
+ }
+ if (sp.id !== undefined && s_gl.buffers[q6 + 5]) {
+ gl.disableVertexAttribArray(sp.id);
+ }
+ }
+
+ //Clean Texture units for IG
+ if (s_gl.imageGeometry != 0 && !x3dom.caps.MOBILE) {
+ gl.activeTexture(gl.TEXTURE0);
+ gl.bindTexture(gl.TEXTURE_2D, null);
+ if (s_gl.imageGeometry == 1) {
+ gl.activeTexture(gl.TEXTURE1);
+ gl.bindTexture(gl.TEXTURE_2D, null);
+ }
+ }
+ }
+
+ if (x3dom.Utils.needLineWidth) {
+ this.stateManager.lineWidth(1);
+ }
+
+ if (depthMode) {
+ this.stateManager.enable(gl.DEPTH_TEST);
+ this.stateManager.depthMask(true);
+ this.stateManager.depthFunc(gl.LEQUAL);
+ this.stateManager.depthRange(0, 1);
+ }
+
+ gl.flush();
+
+ try {
+ // 4 = 1 * 1 * 4; then take width x height window (exception pickRect)
+ var data = new Uint8Array(4 * width * height);
+
+ gl.readPixels(x, y, width, height, gl.RGBA, gl.UNSIGNED_BYTE, data);
+
+ scene._webgl.fboPick.pixelData = data;
+ }
+ catch (se) {
+ scene._webgl.fboPick.pixelData = [];
+ // No Exception on file:// when starting with additional flags:
+ // chrome.exe --disable-web-security
+ x3dom.debug.logException(se + " (cannot pick)");
+ }
+
+ //gl.disable(gl.SCISSOR_TEST);
+
+ this.stateManager.bindFramebuffer(gl.FRAMEBUFFER, null);
+ };
+
+ /*****************************************************************************
+ * Render single Shape
+ *****************************************************************************/
+ Context.prototype.renderShape = function (drawable, viewarea, slights, numLights, mat_view, mat_scene,
+ mat_light, mat_proj, gl)
+ {
+ var shape = drawable.shape;
+ var transform = drawable.transform;
+
+ if (!shape || !shape._webgl || !transform) {
+ x3dom.debug.logError("[Context|RenderShape] No valid Shape!");
+ return;
+ }
+
+ var s_gl = shape._webgl;
+ var sp = s_gl.shader;
+
+ if (!sp) {
+ x3dom.debug.logError("[Context|RenderShape] No Shader is set!");
+ return;
+ }
+
+ var changed = this.stateManager.useProgram(sp);
+
+ //===========================================================================
+ // Set special Geometry variables
+ //===========================================================================
+ var s_app = shape._cf.appearance.node;
+ var s_geo = shape._cf.geometry.node;
+ var s_msh = s_geo._mesh;
+
+ var scene = viewarea._scene;
+ var tex = null;
+
+ if (s_gl.coordType != gl.FLOAT) {
+ if (!s_gl.popGeometry && (x3dom.Utils.isUnsignedType(s_geo._vf.coordType))) {
+ sp.bgCenter = s_geo.getMin().toGL();
+ }
+ else {
+ sp.bgCenter = s_geo._vf.position.toGL();
+ }
+ sp.bgSize = s_geo._vf.size.toGL();
+ sp.bgPrecisionMax = s_geo.getPrecisionMax('coordType');
+ }
+ else {
+ sp.bgCenter = [0, 0, 0];
+ sp.bgSize = [1, 1, 1];
+ sp.bgPrecisionMax = 1;
+ }
+ if (s_gl.colorType != gl.FLOAT) {
+ sp.bgPrecisionColMax = s_geo.getPrecisionMax('colorType');
+ }
+ else {
+ sp.bgPrecisionColMax = 1;
+ }
+ if (s_gl.texCoordType != gl.FLOAT) {
+ sp.bgPrecisionTexMax = s_geo.getPrecisionMax('texCoordType');
+ }
+ else {
+ sp.bgPrecisionTexMax = 1;
+ }
+ if (s_gl.normalType != gl.FLOAT) {
+ sp.bgPrecisionNorMax = s_geo.getPrecisionMax('normalType');
+ }
+ else {
+ sp.bgPrecisionNorMax = 1;
+ }
+
+ if (s_gl.imageGeometry != 0) {
+ sp.IG_bboxMin = s_geo.getMin().toGL();
+ sp.IG_bboxMax = s_geo.getMax().toGL();
+ sp.IG_implicitMeshSize = s_geo._vf.implicitMeshSize.toGL(); // FIXME
+
+ tex = x3dom.Utils.findTextureByName(s_gl.texture, "IG_coords0");
+ if (tex) {
+ sp.IG_coordTextureWidth = tex.texture.width;
+ sp.IG_coordTextureHeight = tex.texture.height;
+ }
+
+ if (s_gl.imageGeometry == 1) {
+ tex = x3dom.Utils.findTextureByName(s_gl.texture, "IG_index");
+ if (tex) {
+ sp.IG_indexTextureWidth = tex.texture.width;
+ sp.IG_indexTextureHeight = tex.texture.height;
+ }
+ }
+ tex = null;
+ }
+
+ //===========================================================================
+ // Set fog
+ //===========================================================================
+ // TODO: when no state/shader switch happens, all light/fog/... uniforms don't need to be set again
+ var fog = scene.getFog();
+
+ // THINKABOUTME: changed flag only works as long as lights and fog are global
+ if (fog && changed) {
+ sp.fogColor = fog._vf.color.toGL();
+ sp.fogRange = fog._vf.visibilityRange;
+ sp.fogType = (fog._vf.fogType == "LINEAR") ? 0.0 : 1.0;
+ }
+
+ //===========================================================================
+ // Set Material
+ //===========================================================================
+ var mat = s_app ? s_app._cf.material.node : null;
+ var shader = s_app ? s_app._shader : null;
+ var twoSidedMat = false;
+
+ var isUserDefinedShader = shader && x3dom.isa(shader, x3dom.nodeTypes.ComposedShader);
+
+ if (s_gl.csshader) {
+ sp.diffuseColor = shader._vf.diffuseFactor.toGL();
+ sp.specularColor = shader._vf.specularFactor.toGL();
+ sp.emissiveColor = shader._vf.emissiveFactor.toGL();
+ sp.shininess = shader._vf.shininessFactor;
+ sp.ambientIntensity = (shader._vf.ambientFactor.x +
+ shader._vf.ambientFactor.y +
+ shader._vf.ambientFactor.z) / 3;
+ sp.transparency = 1.0 - shader._vf.alphaFactor;
+
+ if (shader.getDisplacementMap()) {
+ tex = x3dom.Utils.findTextureByName(s_gl.texture, "displacementMap");
+ sp.displacementWidth = tex.texture.width;
+ sp.displacementHeight = tex.texture.height;
+ sp.displacementFactor = shader._vf.displacementFactor;
+ sp.displacementAxis = (shader._vf.displacementAxis == "x") ? 0.0 :
+ (shader._vf.displacementAxis == "y") ? 1.0 : 2.0;
+ }
+ else if (shader.getDiffuseDisplacementMap()) {
+ tex = x3dom.Utils.findTextureByName(s_gl.texture, "diffuseDisplacementMap");
+ sp.displacementWidth = tex.texture.width;
+ sp.displacementHeight = tex.texture.height;
+ sp.displacementFactor = shader._vf.displacementFactor;
+ sp.displacementAxis = (shader._vf.displacementAxis == "x") ? 0.0 :
+ (shader._vf.displacementAxis == "y") ? 1.0 : 2.0;
+ }
+ if (shader.getMultiDiffuseAlphaMap()) {
+ tex = x3dom.Utils.findTextureByName(s_gl.texture, "multiDiffuseAlphaMap");
+ sp.multiDiffuseAlphaWidth = tex.texture.width;
+ sp.multiDiffuseAlphaHeight = tex.texture.height;
+ }
+ if (shader.getMultiEmissiveAmbientMap()) {
+ tex = x3dom.Utils.findTextureByName(s_gl.texture, "multiEmissiveAmbientMap");
+ sp.multiEmissiveAmbientWidth = tex.texture.width;
+ sp.multiEmissiveAmbientHeight = tex.texture.height;
+ }
+ if (shader.getMultiSpecularShininessMap()) {
+ tex = x3dom.Utils.findTextureByName(s_gl.texture, "multiSpecularShininessMap");
+ sp.multiSpecularShininessWidth = tex.texture.width;
+ sp.multiSpecularShininessHeight = tex.texture.height;
+ }
+ if (shader.getMultiVisibilityMap()) {
+ tex = x3dom.Utils.findTextureByName(s_gl.texture, "multiVisibilityMap");
+ sp.multiVisibilityWidth = tex.texture.width;
+ sp.multiVisibilityHeight = tex.texture.height;
+ }
+ }
+ else if (mat) {
+ sp.diffuseColor = mat._vf.diffuseColor.toGL();
+ sp.specularColor = mat._vf.specularColor.toGL();
+ sp.emissiveColor = mat._vf.emissiveColor.toGL();
+ sp.shininess = mat._vf.shininess;
+ sp.ambientIntensity = mat._vf.ambientIntensity;
+ sp.transparency = mat._vf.transparency;
+ if (x3dom.isa(mat, x3dom.nodeTypes.TwoSidedMaterial)) {
+ twoSidedMat = true;
+ sp.backDiffuseColor = mat._vf.backDiffuseColor.toGL();
+ sp.backSpecularColor = mat._vf.backSpecularColor.toGL();
+ sp.backEmissiveColor = mat._vf.backEmissiveColor.toGL();
+ sp.backShininess = mat._vf.backShininess;
+ sp.backAmbientIntensity = mat._vf.backAmbientIntensity;
+ sp.backTransparency = mat._vf.backTransparency;
+ }
+ }
+ else {
+ sp.diffuseColor = [1.0, 1.0, 1.0];
+ sp.specularColor = [0.0, 0.0, 0.0];
+ sp.emissiveColor = [0.0, 0.0, 0.0];
+ sp.shininess = 0.0;
+ sp.ambientIntensity = 1.0;
+ sp.transparency = 0.0;
+ }
+
+ //Look for user-defined shaders
+ if (shader) {
+ if (isUserDefinedShader) {
+ for (var fName in shader._vf) {
+ if (shader._vf.hasOwnProperty(fName) && fName !== 'language') {
+ var field = shader._vf[fName];
+ if (field) {
+ if (field.toGL) {
+ sp[fName] = field.toGL();
+ }
+ else {
+ sp[fName] = field;
+ }
+ }
+ }
+ }
+ }
+ else if (x3dom.isa(shader, x3dom.nodeTypes.CommonSurfaceShader)) {
+ s_gl.csshader = shader;
+ }
+ }
+
+ //===========================================================================
+ // Set Lights
+ //===========================================================================
+ for (var p = 0; p < numLights && changed; p++) {
+ // FIXME; getCurrentTransform() doesn't work for shared lights/objects!
+ var light_transform = mat_view.mult(slights[p].getCurrentTransform());
+
+ if (x3dom.isa(slights[p], x3dom.nodeTypes.DirectionalLight)) {
+ sp['light' + p + '_Type'] = 0.0;
+ sp['light' + p + '_On'] = (slights[p]._vf.on) ? 1.0 : 0.0;
+ sp['light' + p + '_Color'] = slights[p]._vf.color.toGL();
+ sp['light' + p + '_Intensity'] = slights[p]._vf.intensity;
+ sp['light' + p + '_AmbientIntensity'] = slights[p]._vf.ambientIntensity;
+ sp['light' + p + '_Direction'] = light_transform.multMatrixVec(slights[p]._vf.direction).toGL();
+ sp['light' + p + '_Attenuation'] = [1.0, 1.0, 1.0];
+ sp['light' + p + '_Location'] = [1.0, 1.0, 1.0];
+ sp['light' + p + '_Radius'] = 0.0;
+ sp['light' + p + '_BeamWidth'] = 0.0;
+ sp['light' + p + '_CutOffAngle'] = 0.0;
+ sp['light' + p + '_ShadowIntensity'] = slights[p]._vf.shadowIntensity;
+ }
+ else if (x3dom.isa(slights[p], x3dom.nodeTypes.PointLight)) {
+ sp['light' + p + '_Type'] = 1.0;
+ sp['light' + p + '_On'] = (slights[p]._vf.on) ? 1.0 : 0.0;
+ sp['light' + p + '_Color'] = slights[p]._vf.color.toGL();
+ sp['light' + p + '_Intensity'] = slights[p]._vf.intensity;
+ sp['light' + p + '_AmbientIntensity'] = slights[p]._vf.ambientIntensity;
+ sp['light' + p + '_Direction'] = [1.0, 1.0, 1.0];
+ sp['light' + p + '_Attenuation'] = slights[p]._vf.attenuation.toGL();
+ sp['light' + p + '_Location'] = light_transform.multMatrixPnt(slights[p]._vf.location).toGL();
+ sp['light' + p + '_Radius'] = slights[p]._vf.radius;
+ sp['light' + p + '_BeamWidth'] = 0.0;
+ sp['light' + p + '_CutOffAngle'] = 0.0;
+ sp['light' + p + '_ShadowIntensity'] = slights[p]._vf.shadowIntensity;
+ }
+ else if (x3dom.isa(slights[p], x3dom.nodeTypes.SpotLight)) {
+ sp['light' + p + '_Type'] = 2.0;
+ sp['light' + p + '_On'] = (slights[p]._vf.on) ? 1.0 : 0.0;
+ sp['light' + p + '_Color'] = slights[p]._vf.color.toGL();
+ sp['light' + p + '_Intensity'] = slights[p]._vf.intensity;
+ sp['light' + p + '_AmbientIntensity'] = slights[p]._vf.ambientIntensity;
+ sp['light' + p + '_Direction'] = light_transform.multMatrixVec(slights[p]._vf.direction).toGL();
+ sp['light' + p + '_Attenuation'] = slights[p]._vf.attenuation.toGL();
+ sp['light' + p + '_Location'] = light_transform.multMatrixPnt(slights[p]._vf.location).toGL();
+ sp['light' + p + '_Radius'] = slights[p]._vf.radius;
+ sp['light' + p + '_BeamWidth'] = slights[p]._vf.beamWidth;
+ sp['light' + p + '_CutOffAngle'] = slights[p]._vf.cutOffAngle;
+ sp['light' + p + '_ShadowIntensity'] = slights[p]._vf.shadowIntensity;
+ }
+ }
+
+ //===========================================================================
+ // Set HeadLight
+ //===========================================================================
+ var nav = scene.getNavigationInfo();
+
+ if (nav._vf.headlight && changed) {
+ numLights = (numLights) ? numLights : 0;
+ sp['light' + numLights + '_Type'] = 0.0;
+ sp['light' + numLights + '_On'] = 1.0;
+ sp['light' + numLights + '_Color'] = [1.0, 1.0, 1.0];
+ sp['light' + numLights + '_Intensity'] = 1.0;
+ sp['light' + numLights + '_AmbientIntensity'] = 0.0;
+ sp['light' + numLights + '_Direction'] = [0.0, 0.0, -1.0];
+ sp['light' + numLights + '_Attenuation'] = [1.0, 1.0, 1.0];
+ sp['light' + numLights + '_Location'] = [1.0, 1.0, 1.0];
+ sp['light' + numLights + '_Radius'] = 0.0;
+ sp['light' + numLights + '_BeamWidth'] = 0.0;
+ sp['light' + numLights + '_CutOffAngle'] = 0.0;
+ sp['light' + numLights + '_ShadowIntensity'] = 0.0;
+ }
+
+ //===========================================================================
+ // Set ClipPlanes
+ //===========================================================================
+ if (shape._clipPlanes) {
+ for (var cp = 0; cp < shape._clipPlanes.length; cp++) {
+ var clip_plane = shape._clipPlanes[cp].plane;
+ var clip_trafo = shape._clipPlanes[cp].trafo;
+
+ sp['clipPlane' + cp + '_Plane'] = clip_trafo.multMatrixPlane(clip_plane._vf.plane).toGL();
+ sp['clipPlane' + cp + '_CappingStrength'] = clip_plane._vf.cappingStrength;
+ sp['clipPlane' + cp + '_CappingColor'] = clip_plane._vf.cappingColor.toGL();
+ }
+ }
+
+
+ //===========================================================================
+ // Set DepthMode
+ //===========================================================================
+ var depthMode = s_app ? s_app._cf.depthMode.node : null;
+ if (depthMode)
+ {
+ if (depthMode._vf.enableDepthTest)
+ {
+ //Enable Depth Test
+ this.stateManager.enable(gl.DEPTH_TEST);
+
+ //Set Depth Mask
+ this.stateManager.depthMask(!depthMode._vf.readOnly);
+
+ //Set Depth Function
+ this.stateManager.depthFunc(x3dom.Utils.depthFunc(gl, depthMode._vf.depthFunc));
+
+ //Set Depth Range
+ this.stateManager.depthRange(depthMode._vf.zNearRange, depthMode._vf.zFarRange);
+ }
+ else
+ {
+ //Disable Depth Test
+ this.stateManager.disable(gl.DEPTH_TEST);
+ }
+ }
+ else //Set Defaults
+ {
+ this.stateManager.enable(gl.DEPTH_TEST);
+ this.stateManager.depthMask(true);
+ this.stateManager.depthFunc(gl.LEQUAL);
+ }
+
+ //===========================================================================
+ // Set BlendMode
+ //===========================================================================
+ var blendMode = s_app ? s_app._cf.blendMode.node : null;
+ if (blendMode)
+ {
+ var srcFactor = x3dom.Utils.blendFunc(gl, blendMode._vf.srcFactor);
+ var destFactor = x3dom.Utils.blendFunc(gl, blendMode._vf.destFactor);
+
+ if (srcFactor && destFactor)
+ {
+ //Enable Blending
+ this.stateManager.enable(gl.BLEND);
+
+ //Set Blend Function
+ this.stateManager.blendFuncSeparate(srcFactor, destFactor, gl.ONE, gl.ONE);
+
+ //Set Blend Color
+ this.stateManager.blendColor(blendMode._vf.color.r,
+ blendMode._vf.color.g,
+ blendMode._vf.color.b,
+ 1.0 - blendMode._vf.colorTransparency);
+
+ //Set Blend Equation
+ this.stateManager.blendEquation(x3dom.Utils.blendEquation(gl, blendMode._vf.equation));
+ }
+ else
+ {
+ this.stateManager.disable(gl.BLEND);
+ }
+ }
+ else //Set Defaults
+ {
+ this.stateManager.enable(gl.BLEND);
+ this.stateManager.blendFuncSeparate(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE);
+ }
+
+ //===========================================================================
+ // Set ColorMaskMode
+ //===========================================================================
+ var colorMaskMode = s_app ? s_app._cf.colorMaskMode.node : null;
+ if (colorMaskMode)
+ {
+ this.stateManager.colorMask(colorMaskMode._vf.maskR,
+ colorMaskMode._vf.maskG,
+ colorMaskMode._vf.maskB,
+ colorMaskMode._vf.maskA);
+ }
+ else //Set Defaults
+ {
+ this.stateManager.colorMask(true, true, true, true);
+ }
+
+ //===========================================================================
+ // Set LineProperties (only linewidthScaleFactor, interpreted as lineWidth)
+ //===========================================================================
+ var lineProperties = s_app ? s_app._cf.lineProperties.node : null;
+ if (lineProperties)
+ {
+ this.stateManager.lineWidth(lineProperties._vf.linewidthScaleFactor);
+ }
+ else if (x3dom.Utils.needLineWidth) //Set Defaults
+ {
+ this.stateManager.lineWidth(1);
+ }
+
+ if (shape.isSolid() && !twoSidedMat) {
+ this.stateManager.enable(gl.CULL_FACE);
+
+ if (shape.isCCW()) {
+ this.stateManager.frontFace(gl.CCW);
+ }
+ else {
+ this.stateManager.frontFace(gl.CW);
+ }
+ }
+ else {
+ this.stateManager.disable(gl.CULL_FACE);
+ }
+
+
+ // transformation matrices
+ var model_view = mat_view.mult(transform);
+ var model_view_inv = model_view.inverse();
+
+ sp.modelViewMatrix = model_view.toGL();
+ sp.viewMatrix = mat_view.toGL();
+
+ sp.normalMatrix = model_view_inv.transpose().toGL();
+ sp.modelViewMatrixInverse = model_view_inv.toGL();
+
+ sp.modelViewProjectionMatrix = mat_scene.mult(transform).toGL();
+
+ if (isUserDefinedShader || shape._clipPlanes && shape._clipPlanes.length)
+ {
+ sp.viewMatrixInverse = mat_view.inverse().toGL();
+ }
+
+ // only calculate on "request" (maybe of interest for users)
+ if (isUserDefinedShader) {
+ sp.projectionMatrix = mat_proj.toGL();
+
+ sp.worldMatrix = transform.toGL();
+ sp.worldInverseTranspose = transform.inverse().transpose().toGL();
+
+ }
+
+ //PopGeometry: adapt LOD and set shader variables
+ if (s_gl.popGeometry) {
+ this.updatePopState(drawable, s_geo, sp, s_gl, scene, model_view, viewarea, this.x3dElem.runtime.fps);
+ }
+
+ for (var cnt = 0, cnt_n = s_gl.texture.length; cnt < cnt_n; cnt++) {
+ tex = s_gl.texture[cnt];
+
+ gl.activeTexture(gl.TEXTURE0 + cnt);
+ gl.bindTexture(tex.type, tex.texture);
+ gl.texParameteri(tex.type, gl.TEXTURE_WRAP_S, tex.wrapS);
+ gl.texParameteri(tex.type, gl.TEXTURE_WRAP_T, tex.wrapT);
+ gl.texParameteri(tex.type, gl.TEXTURE_MAG_FILTER, tex.magFilter);
+ gl.texParameteri(tex.type, gl.TEXTURE_MIN_FILTER, tex.minFilter);
+
+ if (!shader || !isUserDefinedShader) {
+ if (!sp[tex.samplerName])
+ sp[tex.samplerName] = cnt;
+ }
+ }
+
+ if (s_app && s_app._cf.textureTransform.node) {
+ var texTrafo = s_app.texTransformMatrix();
+ sp.texTrafoMatrix = texTrafo.toGL();
+ }
+
+
+ // TODO; FIXME; what if geometry with split mesh has dynamic fields?
+ var attrib = null;
+ var df, df_n = s_gl.dynamicFields.length;
+
+ for (df = 0; df < df_n; df++) {
+ attrib = s_gl.dynamicFields[df];
+
+ if (sp[attrib.name] !== undefined) {
+ gl.bindBuffer(gl.ARRAY_BUFFER, attrib.buf);
+
+ gl.vertexAttribPointer(sp[attrib.name], attrib.numComponents, gl.FLOAT, false, 0, 0);
+ gl.enableVertexAttribArray(sp[attrib.name]);
+ }
+ }
+
+ // render object
+ var v, v_n, offset, q_n;
+ var isParticleSet = false;
+
+ if (x3dom.isa(s_geo, x3dom.nodeTypes.ParticleSet)) {
+ isParticleSet = true;
+ }
+
+ if (s_gl.externalGeometry != 0)
+ {
+ q_n = s_gl.primType.length;
+ }
+ else
+ {
+ q_n = s_gl.positions.length;
+ }
+
+ for (var q = 0; q < q_n; q++) {
+ var q6 = 6 * q;
+
+ if ( !(sp.position !== undefined && s_gl.buffers[q6 + 1] && (s_gl.indexes[q] || s_gl.externalGeometry != 0)) )
+ continue;
+
+ if (s_gl.buffers[q6]) {
+ if (isParticleSet && s_geo.drawOrder() != "any") { // sort
+ var indexArray, zPos = [];
+ var pnts = s_geo._cf.coord.node.getPoints();
+ var pn = (pnts.length == s_gl.indexes[q].length) ? s_gl.indexes[q].length : 0;
+
+ for (var i=0; i<pn; i++) {
+ var center = model_view.multMatrixPnt(pnts[i]);
+ zPos.push([i, center.z]);
+ }
+
+ if (s_geo.drawOrder() == "backtofront")
+ zPos.sort(function(a, b) { return a[1] - b[1]; });
+ else
+ zPos.sort(function(b, a) { return a[1] - b[1]; });
+
+ for (i=0; i<pn; i++) {
+ shape._webgl.indexes[q][i] = zPos[i][0];
+ }
+
+ if (x3dom.caps.INDEX_UINT && (pn > 65535)) {
+ indexArray = new Uint32Array(shape._webgl.indexes[q]);
+ shape._webgl.indexType = gl.UNSIGNED_INT;
+ }
+ else {
+ indexArray = new Uint16Array(shape._webgl.indexes[q]);
+ shape._webgl.indexType = gl.UNSIGNED_SHORT;
+ }
+
+ gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, s_gl.buffers[q6]);
+ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indexArray, gl.DYNAMIC_DRAW);
+
+ indexArray = null;
+ }
+
+ gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, s_gl.buffers[q6]);
+ }
+
+ gl.bindBuffer(gl.ARRAY_BUFFER, s_gl.buffers[q6 + 1]);
+
+ gl.vertexAttribPointer(sp.position,
+ s_msh._numPosComponents, s_gl.coordType, false,
+ shape._coordStrideOffset[0], shape._coordStrideOffset[1]);
+ gl.enableVertexAttribArray(sp.position);
+
+ if (sp.normal !== undefined && s_gl.buffers[q6 + 2]) {
+ gl.bindBuffer(gl.ARRAY_BUFFER, s_gl.buffers[q6 + 2]);
+
+ gl.vertexAttribPointer(sp.normal,
+ s_msh._numNormComponents, s_gl.normalType, false,
+ shape._normalStrideOffset[0], shape._normalStrideOffset[1]);
+ gl.enableVertexAttribArray(sp.normal);
+ }
+ if (sp.texcoord !== undefined && s_gl.buffers[q6 + 3]) {
+ gl.bindBuffer(gl.ARRAY_BUFFER, s_gl.buffers[q6 + 3]);
+
+ gl.vertexAttribPointer(sp.texcoord,
+ s_msh._numTexComponents, s_gl.texCoordType, false,
+ shape._texCoordStrideOffset[0], shape._texCoordStrideOffset[1]);
+ gl.enableVertexAttribArray(sp.texcoord);
+ }
+ if (sp.color !== undefined && s_gl.buffers[q6 + 4]) {
+ gl.bindBuffer(gl.ARRAY_BUFFER, s_gl.buffers[q6 + 4]);
+
+ gl.vertexAttribPointer(sp.color,
+ s_msh._numColComponents, s_gl.colorType, false,
+ shape._colorStrideOffset[0], shape._colorStrideOffset[1]);
+ gl.enableVertexAttribArray(sp.color);
+ }
+ if ((sp.id !== undefined || sp.particleSize !== undefined) && s_gl.buffers[q6 + 5]) {
+ gl.bindBuffer(gl.ARRAY_BUFFER, s_gl.buffers[q6 + 5]);
+
+ //texture coordinate hack for IDs
+ if (s_gl.binaryGeometry != 0 && s_geo._vf.idsPerVertex == true)
+ {
+ gl.vertexAttribPointer(sp.id,
+ 1, gl.FLOAT, false, 4, 0);
+ gl.enableVertexAttribArray(sp.id);
+ }
+ else if (isParticleSet)
+ {
+ gl.vertexAttribPointer(sp.particleSize,
+ 3, gl.FLOAT, false, 0, 0);
+ gl.enableVertexAttribArray(sp.particleSize);
+ }
+ }
+ if (s_gl.popGeometry != 0 && s_gl.buffers[q6 + 5]) {
+ //special case: mimic gl_VertexID
+ gl.bindBuffer(gl.ARRAY_BUFFER, s_gl.buffers[q6 + 5]);
+
+ gl.vertexAttribPointer(sp.PG_vertexID, 1, gl.FLOAT, false, 4, 0);
+ gl.enableVertexAttribArray(sp.PG_vertexID);
+ }
+
+ // TODO: implement surface with additional wireframe render mode (independent from poly mode)
+ var indOff, renderMode = viewarea.getRenderMode();
+
+ if (renderMode > 0) {
+ var polyMode = (renderMode == 1) ? gl.POINTS : gl.LINES;
+
+ if (s_gl.binaryGeometry > 0 || s_gl.popGeometry > 0) {
+ for (v = 0, offset = 0, v_n = s_geo._vf.vertexCount.length; v < v_n; v++) {
+ gl.drawElements(polyMode, s_geo._vf.vertexCount[v], s_gl.indexType,
+ x3dom.Utils.getByteAwareOffset(offset, s_gl.indexType, gl));
+ offset += s_geo._vf.vertexCount[v];
+ }
+ }
+ else if (s_gl.binaryGeometry < 0 || s_gl.popGeometry < 0 || s_gl.imageGeometry) {
+ for (v = 0, offset = 0, v_n = s_geo._vf.vertexCount.length; v < v_n; v++) {
+ gl.drawArrays(polyMode, offset, s_geo._vf.vertexCount[v]);
+ offset += s_geo._vf.vertexCount[v];
+ }
+ }
+ //ExternalGeometry: indexed rendering (standard pass, POINTS or LINES)
+ else if (s_gl.externalGeometry == 1)
+ {
+ gl.drawElements(polyMode, s_gl.drawCount[q], s_gl.indexType, s_gl.indexOffset[q]);
+ }
+ //ExternalGeometry: non-indexed rendering (standard pass, POINTS or LINES)
+ else if (s_gl.externalGeometry == -1)
+ {
+ gl.drawArrays(polyMode, 0, s_gl.drawCount[q]);
+ }
+ else if (s_geo.hasIndexOffset()) {
+ // IndexedTriangleStripSet with primType TRIANGLE_STRIP,
+ // and Patch geometry from external BVHRefiner component
+ indOff = shape.tessellationProperties();
+ for (v = 0, v_n = indOff.length; v < v_n; v++) {
+ gl.drawElements(polyMode, indOff[v].count, s_gl.indexType,
+ indOff[v].offset * x3dom.Utils.getOffsetMultiplier(s_gl.indexType, gl));
+ }
+ }
+ else if (s_gl.indexes[q].length == 0) {
+ gl.drawArrays(polyMode, 0, s_gl.positions[q].length / 3);
+ }
+ else {
+ gl.drawElements(polyMode, s_gl.indexes[q].length, s_gl.indexType, 0);
+ }
+ }
+ else {
+ if (s_gl.binaryGeometry > 0 || s_gl.popGeometry > 0) {
+ for (v = 0, offset = 0, v_n = s_geo._vf.vertexCount.length; v < v_n; v++) {
+ gl.drawElements(s_gl.primType[v], s_geo._vf.vertexCount[v], s_gl.indexType,
+ x3dom.Utils.getByteAwareOffset(offset, s_gl.indexType, gl));
+ offset += s_geo._vf.vertexCount[v];
+ }
+ }
+ else if (s_gl.binaryGeometry < 0 || s_gl.popGeometry < 0 || s_gl.imageGeometry) {
+ for (v = 0, offset = 0, v_n = s_geo._vf.vertexCount.length; v < v_n; v++) {
+ gl.drawArrays(s_gl.primType[v], offset, s_geo._vf.vertexCount[v]);
+ offset += s_geo._vf.vertexCount[v];
+ }
+ }
+ //ExternalGeometry: indexed rendering (standard pass)
+ else if (s_gl.externalGeometry == 1)
+ {
+ gl.drawElements(s_gl.primType[q], s_gl.drawCount[q], s_gl.indexType, s_gl.indexOffset[q]);
+ }
+ //ExternalGeometry: non-indexed rendering (standard pass)
+ else if (s_gl.externalGeometry == -1)
+ {
+ gl.drawArrays(s_gl.primType[q], 0, s_gl.drawCount[q]);
+ }
+ else if (s_geo.hasIndexOffset()) {
+ // IndexedTriangleStripSet with primType TRIANGLE_STRIP,
+ // and Patch geometry from external BVHRefiner component
+ indOff = shape.tessellationProperties();
+ for (v = 0, v_n = indOff.length; v < v_n; v++) {
+ gl.drawElements(s_gl.primType, indOff[v].count, s_gl.indexType,
+ indOff[v].offset * x3dom.Utils.getOffsetMultiplier(s_gl.indexType, gl));
+ }
+ }
+ else if (s_gl.indexes[q].length == 0) {
+ gl.drawArrays(s_gl.primType, 0, s_gl.positions[q].length / 3);
+ }
+ else {
+ gl.drawElements(s_gl.primType, s_gl.indexes[q].length, s_gl.indexType, 0);
+ }
+ }
+
+ // disable all used vertex attributes
+ gl.disableVertexAttribArray(sp.position);
+
+ if (sp.normal !== undefined) {
+ gl.disableVertexAttribArray(sp.normal);
+ }
+ if (sp.texcoord !== undefined) {
+ gl.disableVertexAttribArray(sp.texcoord);
+ }
+ if (sp.color !== undefined) {
+ gl.disableVertexAttribArray(sp.color);
+ }
+ if (s_gl.buffers[q6 + 5]) {
+ if (sp.id !== undefined)
+ gl.disableVertexAttribArray(sp.id);
+ else if (sp.particleSize !== undefined)
+ gl.disableVertexAttribArray(sp.particleSize);
+ }
+ if (s_gl.popGeometry != 0 && sp.PG_vertexID !== undefined) {
+ gl.disableVertexAttribArray(sp.PG_vertexID); // mimic gl_VertexID
+ }
+ } // end for loop over attrib arrays
+
+ for (df = 0; df < df_n; df++) {
+ attrib = s_gl.dynamicFields[df];
+
+ if (sp[attrib.name] !== undefined) {
+ gl.disableVertexAttribArray(sp[attrib.name]);
+ }
+ }
+
+ // update stats
+ if (s_gl.imageGeometry) {
+ v_n = s_geo._vf.vertexCount.length;
+ this.numDrawCalls += v_n;
+
+ for (v = 0; v < v_n; v++) {
+ if (s_gl.primType[v] == gl.TRIANGLE_STRIP)
+ this.numFaces += (s_geo._vf.vertexCount[v] - 2);
+ else
+ this.numFaces += (s_geo._vf.vertexCount[v] / 3);
+
+ this.numCoords += s_geo._vf.vertexCount[v];
+ }
+ }
+ else {
+ this.numCoords += s_msh._numCoords;
+ this.numFaces += s_msh._numFaces;
+
+ if (s_gl.binaryGeometry || s_gl.popGeometry) {
+ this.numDrawCalls += s_geo._vf.vertexCount.length;
+ }
+ else if (s_geo.hasIndexOffset()) {
+ this.numDrawCalls += shape.tessellationProperties().length;
+ }
+ else {
+ this.numDrawCalls += q_n;
+ }
+ }
+
+ // reset to default values for possibly user defined render states
+ if (depthMode) {
+ this.stateManager.enable(gl.DEPTH_TEST);
+ this.stateManager.depthMask(true);
+ this.stateManager.depthFunc(gl.LEQUAL);
+ this.stateManager.depthRange(0, 1);
+ }
+
+ if (blendMode) {
+ this.stateManager.enable(gl.BLEND);
+ this.stateManager.blendFuncSeparate(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE);
+ this.stateManager.blendColor(1, 1, 1, 1);
+ this.stateManager.blendEquation(gl.FUNC_ADD);
+ }
+
+ if (colorMaskMode) {
+ this.stateManager.colorMask(true, true, true, true);
+ }
+
+ if (lineProperties) {
+ this.stateManager.lineWidth(1);
+ }
+
+ // cleanup textures
+ var s_gl_tex = s_gl.texture;
+ cnt_n = s_gl_tex ? s_gl_tex.length : 0;
+
+ for (cnt = 0; cnt < cnt_n; cnt++) {
+ if (!s_gl_tex[cnt])
+ continue;
+
+ if (s_app && s_app._cf.texture.node) {
+ tex = s_app._cf.texture.node.getTexture(cnt);
+ gl.activeTexture(gl.TEXTURE0 + cnt);
+
+ if (x3dom.isa(tex, x3dom.nodeTypes.X3DEnvironmentTextureNode)) {
+ gl.bindTexture(gl.TEXTURE_CUBE_MAP, null);
+ }
+ else {
+ gl.bindTexture(gl.TEXTURE_2D, null);
+ }
+ }
+ }
+ };
+
+ /*****************************************************************************
+ * PopGeometry: adapt LOD and set shader variables
+ *****************************************************************************/
+ Context.prototype.updatePopState = function (drawable, popGeo, sp, s_gl, scene, model_view, viewarea, currFps)
+ {
+ var tol = x3dom.nodeTypes.PopGeometry.ErrorToleranceFactor * popGeo._vf.precisionFactor;
+
+ if (currFps <= 1 || viewarea.isMovingOrAnimating()) {
+ tol *= x3dom.nodeTypes.PopGeometry.PrecisionFactorOnMove;
+ }
+
+ var currentLOD = 16;
+
+ if (tol > 0) {
+
+ //BEGIN CLASSIC CODE
+ var viewpoint = scene.getViewpoint();
+ var imgPlaneHeightAtDistOne = viewpoint.getImgPlaneHeightAtDistOne();
+ var near = viewpoint.getNear();
+ var center = model_view.multMatrixPnt(popGeo._vf.position);
+
+ var tightRad = model_view.multMatrixVec(popGeo._vf.size).length() * 0.5;
+ var largestRad = model_view.multMatrixVec(popGeo._vf.maxBBSize).length() * 0.5;
+
+ //distance is estimated conservatively using the bounding sphere
+ var dist = Math.max(-center.z - tightRad, near);
+ var projPixelLength = dist * (imgPlaneHeightAtDistOne / viewarea._height);
+
+ //compute LOD using bounding sphere
+ var arg = (2 * largestRad) / (tol * projPixelLength);
+ //END CLASSIC CODE
+
+ //BEGIN EXPERIMENTAL CODE
+ //compute LOD using screen-space coverage of bounding sphere
+ //@todo: the coverage should be distinct from priority
+ //var cov = drawable.priority;
+ //@todo: here, we need to decide whether we want to keep the ModF-encoding with
+ // respect to the largest bounding box... if not, change this and the shaders
+ //cov *= (popGeo._vf.maxBBSize.length() / popGeo._vf.size.length());
+ //var arg = cov / tol;
+ //END EXPERIMENTAL CODE
+
+ // use precomputed log(2.0) = 0.693147180559945
+ currentLOD = Math.ceil(Math.log(arg) / 0.693147180559945);
+ currentLOD = (currentLOD < 1) ? 1 : ((currentLOD > 16) ? 16 : currentLOD);
+ }
+
+ //take care of user-controlled min and max values
+ var minPrec = popGeo._vf.minPrecisionLevel, maxPrec = popGeo._vf.maxPrecisionLevel;
+
+ currentLOD = (minPrec != -1 && currentLOD < minPrec) ? minPrec : currentLOD;
+ currentLOD = (maxPrec != -1 && currentLOD > maxPrec) ? maxPrec : currentLOD;
+
+ //assign rendering resolution, according to currently loaded data and LOD
+ var currentLOD_min = (s_gl.levelsAvailable < currentLOD) ? s_gl.levelsAvailable : currentLOD;
+ currentLOD = currentLOD_min;
+
+ //@todo: only for demonstration purposes!!!
+ if (tol <= 1)
+ currentLOD = (currentLOD == popGeo.getNumLevels()) ? 16 : currentLOD;
+
+ //here, we tell X3DOM how many faces / vertices get displayed in the stats
+ var hasIndex = popGeo._vf.indexedRendering;
+ var p_msh = popGeo._mesh;
+
+ p_msh._numCoords = 0;
+ p_msh._numFaces = 0;
+
+ //@todo: this assumes pure TRIANGLES data (and gets overwritten from shadow/picking pass!!!)
+ for (var i = 0; i < currentLOD_min; ++i) { // currentLOD breaks loop
+ var numVerticesAtLevel_i = s_gl.numVerticesAtLevel[i];
+ p_msh._numCoords += numVerticesAtLevel_i;
+ p_msh._numFaces += (hasIndex ? popGeo.getNumIndicesByLevel(i) : numVerticesAtLevel_i) / 3;
+ }
+
+ x3dom.nodeTypes.PopGeometry.numRenderedVerts += p_msh._numCoords;
+ x3dom.nodeTypes.PopGeometry.numRenderedTris += p_msh._numFaces;
+
+ //this field is mainly thought for the use with external statistics
+ //@todo: does not work with instances
+ p_msh.currentLOD = currentLOD;
+
+ //here, we tell X3DOM how many vertices get rendered
+ //@todo: this assumes pure TRIANGLES data
+ popGeo.adaptVertexCount(hasIndex ? p_msh._numFaces * 3 : p_msh._numCoords);
+
+ // finally set shader variables...
+ sp.PG_maxBBSize = popGeo._vf.maxBBSize.toGL();
+
+ sp.PG_bbMin = popGeo._bbMinBySize; // floor(bbMin / maxBBSize)
+
+ sp.PG_numAnchorVertices = popGeo._vf.numAnchorVertices;
+
+ sp.PG_bbMaxModF = popGeo._vf.bbMaxModF.toGL();
+ sp.PG_bboxShiftVec = popGeo._vf.bbShiftVec.toGL();
+
+ sp.PG_precisionLevel = currentLOD;
+
+ //mimics Math.pow(2.0, 16.0 - currentLOD);
+ sp.PG_powPrecision = x3dom.nodeTypes.PopGeometry.powLUT[currentLOD - 1];
+ };
+
+
+ /*****************************************************************************
+ * Render ColorBuffer-Pass for picking
+ *****************************************************************************/
+ Context.prototype.pickValue = function (viewarea, x, y, buttonState, viewMat, sceneMat)
+ {
+ x3dom.Utils.startMeasure("picking");
+
+ var scene = viewarea._scene;
+
+ var gl = this.ctx3d;
+
+ // method requires that scene has already been rendered at least once
+ if (!gl || !scene || !scene._webgl || !scene.drawableCollection) {
+ return false;
+ }
+
+ var pm = scene._vf.pickMode.toLowerCase();
+ var pickMode = 0;
+
+ switch (pm) {
+ case "box": return false;
+ case "idbuf": pickMode = 0; break;
+ case "idbuf24": pickMode = 3; break;
+ case "idbufid": pickMode = 4; break;
+ case "color": pickMode = 1; break;
+ case "texcoord": pickMode = 2; break;
+ }
+
+
+ // ViewMatrix and ViewProjectionMatrix
+ var mat_view, mat_scene;
+
+ if (arguments.length > 4) {
+ mat_view = viewMat;
+ mat_scene = sceneMat;
+ }
+ else {
+ mat_view = viewarea._last_mat_view;
+ mat_scene = viewarea._last_mat_scene;
+ }
+
+ // remember correct scene bbox
+ var min = x3dom.fields.SFVec3f.copy(scene._lastMin);
+ var max = x3dom.fields.SFVec3f.copy(scene._lastMax);
+ // get current camera position
+ var from = mat_view.inverse().e3();
+
+ // get bbox of scene bbox and camera position
+ var _min = x3dom.fields.SFVec3f.copy(from);
+ var _max = x3dom.fields.SFVec3f.copy(from);
+
+ if (_min.x > min.x) { _min.x = min.x; }
+ if (_min.y > min.y) { _min.y = min.y; }
+ if (_min.z > min.z) { _min.z = min.z; }
+
+ if (_max.x < max.x) { _max.x = max.x; }
+ if (_max.y < max.y) { _max.y = max.y; }
+ if (_max.z < max.z) { _max.z = max.z; }
+
+ // temporarily set scene size to include camera
+ scene._lastMin.setValues(_min);
+ scene._lastMax.setValues(_max);
+
+ // get scalar scene size and adapted projection matrix
+ var sceneSize = scene._lastMax.subtract(scene._lastMin).length();
+ var cctowc = viewarea.getCCtoWCMatrix();
+
+ // restore correct scene bbox
+ scene._lastMin.setValues(min);
+ scene._lastMax.setValues(max);
+
+ // for deriving shadow ids together with shape ids
+ var baseID = x3dom.nodeTypes.Shape.objectID + 2;
+
+
+ // render to texture for reading pixel values
+ this.renderPickingPass(gl, scene, mat_view, mat_scene, from, sceneSize, pickMode, x, y, 2, 2);
+
+ // the pixel values under mouse cursor
+ var pixelData = scene._webgl.fboPick.pixelData;
+
+ if (pixelData && pixelData.length)
+ {
+ var pickPos = new x3dom.fields.SFVec3f(0, 0, 0);
+ var pickNorm = new x3dom.fields.SFVec3f(0, 0, 1);
+
+ var index = 0;
+ var objId = pixelData[index + 3], shapeId;
+
+ var pixelOffset = 1.0 / scene._webgl.pickScale;
+ var denom = 1.0 / 256.0;
+ var dist, line, lineoff, right, up;
+
+ if (pickMode == 0) {
+ objId += 256 * pixelData[index + 2];
+
+ dist = (pixelData[index ] / 255.0) * denom +
+ (pixelData[index + 1] / 255.0);
+
+ line = viewarea.calcViewRay(x, y, cctowc);
+
+ pickPos = line.pos.add(line.dir.multiply(dist * sceneSize));
+
+ index = 4; // get right pixel
+ dist = (pixelData[index ] / 255.0) * denom +
+ (pixelData[index + 1] / 255.0);
+
+ lineoff = viewarea.calcViewRay(x + pixelOffset, y, cctowc);
+
+ right = lineoff.pos.add(lineoff.dir.multiply(dist * sceneSize));
+ right = right.subtract(pickPos).normalize();
+
+ index = 8; // get top pixel
+ dist = (pixelData[index ] / 255.0) * denom +
+ (pixelData[index + 1] / 255.0);
+
+ lineoff = viewarea.calcViewRay(x, y - pixelOffset, cctowc);
+
+ up = lineoff.pos.add(lineoff.dir.multiply(dist * sceneSize));
+ up = up.subtract(pickPos).normalize();
+
+ pickNorm = right.cross(up).normalize();
+ }
+ else if (pickMode == 3) {
+ objId += 256 * pixelData[index + 2] +
+ 65536 * pixelData[index + 1];
+
+ dist = pixelData[index] / 255.0;
+
+ line = viewarea.calcViewRay(x, y, cctowc);
+
+ pickPos = line.pos.add(line.dir.multiply(dist * sceneSize));
+
+ index = 4; // get right pixel
+ dist = pixelData[index] / 255.0;
+
+ lineoff = viewarea.calcViewRay(x + pixelOffset, y, cctowc);
+
+ right = lineoff.pos.add(lineoff.dir.multiply(dist * sceneSize));
+ right = right.subtract(pickPos).normalize();
+
+ index = 8; // get top pixel
+ dist = pixelData[index] / 255.0;
+
+ lineoff = viewarea.calcViewRay(x, y - pixelOffset, cctowc);
+
+ up = lineoff.pos.add(lineoff.dir.multiply(dist * sceneSize));
+ up = up.subtract(pickPos).normalize();
+
+ pickNorm = right.cross(up).normalize();
+ }
+ else if (pickMode == 4) {
+ objId += 256 * pixelData[index + 2];
+
+ shapeId = pixelData[index + 1];
+ shapeId += 256 * pixelData[index ];
+
+ // check if standard shape picked without special shadow id
+ if (objId == 0 && (shapeId > 0 && shapeId < baseID)) {
+ objId = shapeId;
+ }
+ }
+ else {
+ pickPos.x = pixelData[index ];
+ pickPos.y = pixelData[index + 1];
+ pickPos.z = pixelData[index + 2];
+ }
+ //x3dom.debug.logInfo(pickPos + " / " + objId);
+
+ var eventType = "shadowObjectIdChanged";
+ var shadowObjectIdChanged, event;
+ var button = Math.max(buttonState >>> 8, buttonState & 255);
+
+ if (objId >= baseID) {
+ objId -= baseID;
+
+ var hitObject;
+
+ if (pickMode != 4) {
+ viewarea._pickingInfo.pickPos = pickPos;
+ viewarea._pick.setValues(pickPos);
+
+ viewarea._pickingInfo.pickNorm = pickNorm;
+ viewarea._pickNorm.setValues(pickNorm);
+
+ viewarea._pickingInfo.pickObj = null;
+ viewarea._pickingInfo.lastClickObj = null;
+
+ hitObject = scene._xmlNode;
+ }
+ else {
+ viewarea._pickingInfo.pickObj = x3dom.nodeTypes.Shape.idMap.nodeID[shapeId];
+
+ hitObject = viewarea._pickingInfo.pickObj._xmlNode;
+ }
+
+
+ //Check if there are MultiParts
+ if (scene._multiPartMap) {
+ var mp, multiPart;
+
+ //Find related MultiPart
+ for (mp=0; mp<scene._multiPartMap.multiParts.length; mp++)
+ {
+ multiPart = scene._multiPartMap.multiParts[mp];
+ if (objId >= multiPart._minId && objId <= multiPart._maxId)
+ {
+ hitObject = multiPart._xmlNode;
+
+ event = {
+ target: multiPart._xmlNode,
+ button: button, mouseup: ((buttonState >>> 8) > 0),
+ layerX: x, layerY: y,
+ pickedId: objId,
+ worldX: pickPos.x, worldY: pickPos.y, worldZ: pickPos.z,
+ normalX: pickNorm.x, normalY: pickNorm.y, normalZ: pickNorm.z,
+ hitPnt: pickPos.toGL(),
+ hitObject: hitObject,
+ cancelBubble: false,
+ stopPropagation: function () { this.cancelBubble = true; },
+ preventDefault: function () { this.cancelBubble = true; }
+ };
+
+ multiPart.handleEvents(event);
+ }
+ else
+ {
+ event = {
+ target: multiPart._xmlNode,
+ button: button, mouseup: ((buttonState >>> 8) > 0),
+ layerX: x, layerY: y,
+ pickedId: -1,
+ cancelBubble: false,
+ stopPropagation: function () { this.cancelBubble = true; },
+ preventDefault: function () { this.cancelBubble = true; }
+ };
+
+ multiPart.handleEvents(event);
+ }
+ }
+ }
+
+ shadowObjectIdChanged = (viewarea._pickingInfo.shadowObjectId != objId);
+ viewarea._pickingInfo.lastShadowObjectId = viewarea._pickingInfo.shadowObjectId;
+ viewarea._pickingInfo.shadowObjectId = objId;
+ //x3dom.debug.logInfo(baseID + " + " + objId);
+
+ if ((shadowObjectIdChanged || button) && scene._xmlNode &&
+ (scene._xmlNode["on" + eventType] || scene._xmlNode.hasAttribute("on" + eventType) ||
+ scene._listeners[eventType]))
+ {
+ event = {
+ target: scene._xmlNode,
+ type: eventType,
+ button: button, mouseup: ((buttonState >>> 8) > 0),
+ layerX: x, layerY: y,
+ shadowObjectId: objId,
+ worldX: pickPos.x, worldY: pickPos.y, worldZ: pickPos.z,
+ normalX: pickNorm.x, normalY: pickNorm.y, normalZ: pickNorm.z,
+ hitPnt: pickPos.toGL(),
+ hitObject: hitObject,
+ cancelBubble: false,
+ stopPropagation: function () { this.cancelBubble = true; },
+ preventDefault: function () { this.cancelBubble = true; }
+ };
+ scene.callEvtHandler(("on" + eventType), event);
+ }
+
+ if (scene._shadowIdMap && scene._shadowIdMap.mapping &&
+ objId < scene._shadowIdMap.mapping.length) {
+ var shIds = scene._shadowIdMap.mapping[objId].usage;
+ var n, c, shObj;
+
+ if (!line) {
+ line = viewarea.calcViewRay(x, y, cctowc);
+ }
+ // find corresponding dom tree object
+ for (c = 0; c < shIds.length; c++) {
+ shObj = scene._nameSpace.defMap[shIds[c]];
+ // FIXME; bbox test too coarse (+ should include trafo)
+ if (shObj && shObj.doIntersect(line)) {
+ viewarea._pickingInfo.pickObj = shObj;
+ break;
+ }
+ }
+ //Check for other namespaces e.g. Inline/Multipart (FIXME; check recursively)
+ for (n = 0; n<scene._nameSpace.childSpaces.length; n++)
+ {
+ for (c = 0; c < shIds.length; c++) {
+ shObj = scene._nameSpace.childSpaces[n].defMap[shIds[c]];
+ // FIXME; bbox test too coarse (+ should include trafo)
+ if (shObj && shObj.doIntersect(line)) {
+ viewarea._pickingInfo.pickObj = shObj;
+ break;
+ }
+ }
+ }
+ }
+ }
+ else {
+ //Check if there are MultiParts
+ if (scene._multiPartMap) {
+
+ //Find related MultiPart
+ for (mp=0; mp<scene._multiPartMap.multiParts.length; mp++)
+ {
+ multiPart = scene._multiPartMap.multiParts[mp];
+
+ event = {
+ target: multiPart._xmlNode,
+ button: button, mouseup: ((buttonState >>> 8) > 0),
+ layerX: x, layerY: y,
+ pickedId: -1,
+ cancelBubble: false,
+ stopPropagation: function () { this.cancelBubble = true; },
+ preventDefault: function () { this.cancelBubble = true; }
+ };
+
+ multiPart.handleEvents(event);
+ }
+ }
+
+
+ shadowObjectIdChanged = (viewarea._pickingInfo.shadowObjectId != -1);
+ viewarea._pickingInfo.shadowObjectId = -1; // nothing hit
+
+ if ( shadowObjectIdChanged && scene._xmlNode &&
+ (scene._xmlNode["on" + eventType] || scene._xmlNode.hasAttribute("on" + eventType) ||
+ scene._listeners[eventType]) )
+ {
+ event = {
+ target: scene._xmlNode,
+ type: eventType,
+ button: button, mouseup: ((buttonState >>> 8) > 0),
+ layerX: x, layerY: y,
+ shadowObjectId: viewarea._pickingInfo.shadowObjectId,
+ cancelBubble: false,
+ stopPropagation: function () { this.cancelBubble = true; },
+ preventDefault: function () { this.cancelBubble = true; }
+ };
+ scene.callEvtHandler(("on" + eventType), event);
+ }
+
+ if (objId > 0) {
+ //x3dom.debug.logInfo(x3dom.nodeTypes.Shape.idMap.nodeID[objId]._DEF + " // " +
+ // x3dom.nodeTypes.Shape.idMap.nodeID[objId]._xmlNode.localName);
+ viewarea._pickingInfo.pickPos = pickPos;
+ viewarea._pickingInfo.pickNorm = pickNorm;
+ viewarea._pickingInfo.pickObj = x3dom.nodeTypes.Shape.idMap.nodeID[objId];
+ }
+ else {
+ viewarea._pickingInfo.pickObj = null;
+ //viewarea._pickingInfo.lastObj = null;
+ viewarea._pickingInfo.lastClickObj = null;
+ }
+ }
+ }
+ var pickTime = x3dom.Utils.stopMeasure("picking");
+ this.x3dElem.runtime.addMeasurement('PICKING', pickTime);
+
+ return true;
+ };
+
+ /*****************************************************************************
+ * Render ColorBuffer-Pass for picking sub window
+ *****************************************************************************/
+ Context.prototype.pickRect = function (viewarea, x1, y1, x2, y2)
+ {
+ var gl = this.ctx3d;
+ var scene = viewarea ? viewarea._scene : null;
+
+ // method requires that scene has already been rendered at least once
+ if (!gl || !scene || !scene._webgl || !scene.drawableCollection)
+ return false;
+
+ // values not fully correct but unnecessary anyway, just to feed the shader
+ var from = viewarea._last_mat_view.inverse().e3();
+ var sceneSize = scene._lastMax.subtract(scene._lastMin).length();
+
+ var x = (x1 <= x2) ? x1 : x2;
+ var y = (y1 >= y2) ? y1 : y2;
+ var width = (1 + Math.abs(x2 - x1)) * scene._webgl.pickScale;
+ var height = (1 + Math.abs(y2 - y1)) * scene._webgl.pickScale;
+
+ // render to texture for reading pixel values
+ this.renderPickingPass(gl, scene, viewarea._last_mat_view, viewarea._last_mat_scene,
+ from, sceneSize, 0, x, y, (width < 1) ? 1 : width, (height < 1) ? 1 : height);
+
+ var index;
+ var pickedObjects = [];
+
+ // get objects in rectangle
+ for (index = 0; scene._webgl.fboPick.pixelData &&
+ index < scene._webgl.fboPick.pixelData.length; index += 4) {
+ var objId = scene._webgl.fboPick.pixelData[index + 3] +
+ scene._webgl.fboPick.pixelData[index + 2] * 256;
+
+ if (objId > 0)
+ pickedObjects.push(objId);
+ }
+ pickedObjects.sort();
+
+ // make found object IDs unique
+ var pickedObjectsTemp = (function (arr) {
+ var a = [], l = arr.length;
+ for (var i = 0; i < l; i++) {
+ for (var j = i + 1; j < l; j++) {
+ if (arr[i] === arr[j])
+ j = ++i;
+ }
+ a.push(arr[i]);
+ }
+ return a;
+ })(pickedObjects);
+ pickedObjects = pickedObjectsTemp;
+
+ var pickedNodes = [];
+
+ var hitObject;
+
+ // for deriving shadow ids together with shape ids
+ var baseID = x3dom.nodeTypes.Shape.objectID + 2;
+
+ for (index = 0; index < pickedObjects.length; index++) {
+ objId = pickedObjects[index];
+
+ if (objId >= baseID)
+ {
+ objId -= baseID;
+
+ //Check if there are MultiParts
+ if (scene._multiPartMap) {
+ var mp, multiPart, colorMap, emissiveMap, specularMap, visibilityMap;
+
+ //Find related MultiPart
+ for (mp = 0; mp < scene._multiPartMap.multiParts.length; mp++) {
+ multiPart = scene._multiPartMap.multiParts[mp];
+ colorMap = multiPart._inlineNamespace.defMap["MultiMaterial_ColorMap"];
+ emissiveMap = multiPart._inlineNamespace.defMap["MultiMaterial_EmissiveMap"];
+ specularMap = multiPart._inlineNamespace.defMap["MultiMaterial_SpecularMap"];
+ visibilityMap = multiPart._inlineNamespace.defMap["MultiMaterial_VisibilityMap"];
+ if (objId >= multiPart._minId && objId <= multiPart._maxId) {
+ hitObject = new x3dom.Parts(multiPart, [objId], colorMap, emissiveMap, specularMap, visibilityMap);
+ pickedNodes.push(hitObject);
+ }
+ }
+ }
+
+ }
+ else
+ {
+ hitObject = x3dom.nodeTypes.Shape.idMap.nodeID[objId];
+ hitObject = (hitObject && hitObject._xmlNode) ? hitObject._xmlNode : null;
+
+ if (hitObject)
+ pickedNodes.push(hitObject);
+ }
+ }
+
+ return pickedNodes;
+ };
+
+ /*****************************************************************************
+ * Render Scene (Main-Pass)
+ *****************************************************************************/
+ Context.prototype.renderScene = function (viewarea)
+ {
+ var gl = this.ctx3d;
+ var scene = viewarea._scene;
+
+ if (gl === null || scene === null) {
+ return;
+ }
+
+ var rentex = viewarea._doc._nodeBag.renderTextures;
+ var rt_tex, rtl_i, rtl_n = rentex.length;
+ var texProp = null;
+
+ // for initFBO
+ var type = gl.UNSIGNED_BYTE;
+ var shadowType = gl.UNSIGNED_BYTE;
+ var nearestFilt = false;
+
+ if (x3dom.caps.FP_TEXTURES && !x3dom.caps.MOBILE) {
+ type = gl.FLOAT;
+ shadowType = gl.FLOAT;
+ if (!x3dom.caps.FPL_TEXTURES) {
+ nearestFilt = true; // TODO: use correct filtering for fp-textures
+ }
+ }
+
+ var shadowedLights, numShadowMaps;
+ var i, j, n, size, sizeAvailable;
+ var texType, refinementPos;
+ var vertices = [-1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, 1];
+
+ scene.updateVolume();
+
+ if (!scene._webgl)
+ {
+ scene._webgl = {};
+
+ this.setupFgnds(gl, scene);
+
+ // scale factor for mouse coords and width/ height (low res for speed-up)
+ scene._webgl.pickScale = 0.5;
+
+ scene._webgl._currFboWidth = Math.round(this.canvas.width * scene._webgl.pickScale);
+ scene._webgl._currFboHeight = Math.round(this.canvas.height * scene._webgl.pickScale);
+
+ // TODO: FIXME when spec ready: readPixels not (yet?) available for float textures
+ // https://bugzilla.mozilla.org/show_bug.cgi?id=681903
+ // https://www.khronos.org/webgl/public-mailing-list/archives/1108/msg00025.html
+ scene._webgl.fboPick = x3dom.Utils.initFBO(gl,
+ scene._webgl._currFboWidth, scene._webgl._currFboHeight, gl.UNSIGNED_BYTE, false, true);
+ scene._webgl.fboPick.pixelData = null;
+
+ //Set picking shaders
+ /*scene._webgl.pickShader = this.cache.getShader(gl, x3dom.shader.PICKING);
+ scene._webgl.pickShader24 = this.cache.getShader(gl, x3dom.shader.PICKING_24);
+ scene._webgl.pickShaderId = this.cache.getShader(gl, x3dom.shader.PICKING_ID);
+ scene._webgl.pickColorShader = this.cache.getShader(gl, x3dom.shader.PICKING_COLOR);
+ scene._webgl.pickTexCoordShader = this.cache.getShader(gl, x3dom.shader.PICKING_TEXCOORD);*/
+
+ scene._webgl.normalShader = this.cache.getShader(gl, x3dom.shader.NORMAL);
+
+ //Initialize shadow maps
+ scene._webgl.fboShadow = [];
+
+ shadowedLights = viewarea.getShadowedLights();
+ n = shadowedLights.length;
+
+ for (i=0; i<n; i++)
+ {
+ size = shadowedLights[i]._vf.shadowMapSize;
+
+ if (!x3dom.isa(shadowedLights[i], x3dom.nodeTypes.PointLight))
+ //cascades for directional lights
+ numShadowMaps = Math.max(1,Math.min(shadowedLights[i]._vf.shadowCascades,6));
+ else
+ //six maps for point lights
+ numShadowMaps = 6;
+
+ scene._webgl.fboShadow[i] = [];
+
+ for (j=0; j < numShadowMaps; j++)
+ scene._webgl.fboShadow[i][j] = x3dom.Utils.initFBO(gl, size, size, shadowType, false, true);
+ }
+
+ if (scene._webgl.fboShadow.length > 0 || x3dom.SSAO.isEnabled(scene))
+ scene._webgl.fboScene = x3dom.Utils.initFBO(gl, this.canvas.width, this.canvas.height, shadowType, false, true);
+ scene._webgl.fboBlur = [];
+
+ //initialize blur fbo (different fbos for different sizes)
+ for (i=0; i<n; i++)
+ {
+ size = scene._webgl.fboShadow[i][0].height;
+ sizeAvailable = false;
+
+ for (j = 0; j < scene._webgl.fboBlur.length; j++){
+ if (size == scene._webgl.fboBlur[j].height)
+ sizeAvailable = true;
+ }
+ if (!sizeAvailable)
+ scene._webgl.fboBlur[scene._webgl.fboBlur.length] = x3dom.Utils.initFBO(gl, size, size, shadowType, false, true);
+ }
+
+ //initialize Data for post processing
+ scene._webgl.ppBuffer = gl.createBuffer();
+ gl.bindBuffer(gl.ARRAY_BUFFER, scene._webgl.ppBuffer);
+ gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);
+
+ scene._webgl.shadowShader = this.cache.getShader(gl, x3dom.shader.SHADOW);
+
+ // TODO; cleanup on shutdown and lazily create on first use like size-dependent variables below
+ scene._webgl.refinement = {
+ stamps: new Array(2),
+ positionBuffer: gl.createBuffer()
+ };
+ gl.bindBuffer(gl.ARRAY_BUFFER, scene._webgl.refinement.positionBuffer);
+ gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);
+
+ // This must be refreshed on node change!
+ for (rtl_i = 0; rtl_i < rtl_n; rtl_i++) {
+ rt_tex = rentex[rtl_i];
+
+ texProp = rt_tex._cf.textureProperties.node;
+ texType = rt_tex.requirePingPong() ? gl.UNSIGNED_BYTE : type;
+ rt_tex._webgl = {};
+ rt_tex._webgl.fbo = x3dom.Utils.initFBO(gl,
+ rt_tex._vf.dimensions[0], rt_tex._vf.dimensions[1], texType,
+ (texProp && texProp._vf.generateMipMaps), !rt_tex.requirePingPong());
+
+ rt_tex._cleanupGLObjects = function(retainTex) {
+ if (!retainTex)
+ gl.deleteTexture(this._webgl.fbo.tex);
+ if (this._webgl.fbo.rbo)
+ gl.deleteRenderbuffer(this._webgl.fbo.rbo);
+ gl.bindFramebuffer(gl.FRAMEBUFFER, null);
+ gl.deleteFramebuffer(this._webgl.fbo.fbo);
+ this._webgl.fbo.rbo = null;
+ this._webgl.fbo.fbo = null;
+ };
+
+ if (rt_tex.requirePingPong()) {
+ refinementPos = rt_tex._vf.dimensions[0] + "x" + rt_tex._vf.dimensions[1];
+ if (scene._webgl.refinement[refinementPos] === undefined) {
+ scene._webgl.refinement[refinementPos] = x3dom.Utils.initFBO(gl,
+ rt_tex._vf.dimensions[0], rt_tex._vf.dimensions[1], texType, false, false);
+ }
+ rt_tex._webgl.texture = null;
+ }
+ }
+
+ viewarea._last_mat_view = x3dom.fields.SFMatrix4f.identity();
+ viewarea._last_mat_proj = x3dom.fields.SFMatrix4f.identity();
+ viewarea._last_mat_scene = x3dom.fields.SFMatrix4f.identity();
+
+ this._calledViewpointChangedHandler = false;
+ }
+ else // updates needed?
+ {
+ var fboWidth = Math.round(this.canvas.width * scene._webgl.pickScale);
+ var fboHeight = Math.round(this.canvas.height * scene._webgl.pickScale);
+
+ if (scene._webgl._currFboWidth !== fboWidth ||
+ scene._webgl._currFboHeight !== fboHeight) {
+ scene._webgl._currFboWidth = fboWidth;
+ scene._webgl._currFboHeight = fboHeight;
+
+ scene._webgl.fboPick = x3dom.Utils.initFBO(gl, fboWidth, fboHeight, scene._webgl.fboPick.type, false, true);
+ scene._webgl.fboPick.pixelData = null;
+
+ x3dom.debug.logInfo("Refreshed picking FBO to size (" + fboWidth + ", " + fboHeight + ")");
+ }
+
+ for (rtl_i = 0; rtl_i < rtl_n; rtl_i++) {
+ rt_tex = rentex[rtl_i];
+ if (rt_tex._webgl && rt_tex._webgl.fbo &&
+ rt_tex._webgl.fbo.width == rt_tex._vf.dimensions[0] &&
+ rt_tex._webgl.fbo.height == rt_tex._vf.dimensions[1])
+ continue;
+
+ rt_tex.invalidateGLObject();
+ if (rt_tex._cleanupGLObjects)
+ rt_tex._cleanupGLObjects();
+ else
+ rt_tex._cleanupGLObjects = function(retainTex) {
+ if (!retainTex)
+ gl.deleteTexture(this._webgl.fbo.tex);
+ if (this._webgl.fbo.rbo)
+ gl.deleteRenderbuffer(this._webgl.fbo.rbo);
+ gl.bindFramebuffer(gl.FRAMEBUFFER, null);
+ gl.deleteFramebuffer(this._webgl.fbo.fbo);
+ this._webgl.fbo.rbo = null;
+ this._webgl.fbo.fbo = null;
+ };
+
+ texProp = rt_tex._cf.textureProperties.node;
+ texType = rt_tex.requirePingPong() ? gl.UNSIGNED_BYTE : type;
+ rt_tex._webgl = {};
+ rt_tex._webgl.fbo = x3dom.Utils.initFBO(gl,
+ rt_tex._vf.dimensions[0], rt_tex._vf.dimensions[1], texType,
+ (texProp && texProp._vf.generateMipMaps), !rt_tex.requirePingPong());
+
+ if (rt_tex.requirePingPong()) {
+ refinementPos = rt_tex._vf.dimensions[0] + "x" + rt_tex._vf.dimensions[1];
+ if (scene._webgl.refinement[refinementPos] === undefined) {
+ scene._webgl.refinement[refinementPos] = x3dom.Utils.initFBO(gl,
+ rt_tex._vf.dimensions[0], rt_tex._vf.dimensions[1], texType, false, false);
+ }
+ rt_tex._webgl.texture = null;
+ }
+
+ x3dom.debug.logInfo("Init/resize RenderedTexture_" + rtl_i + " to size " +
+ rt_tex._vf.dimensions[0] + " x " + rt_tex._vf.dimensions[1]);
+ }
+
+ //reinitialize shadow fbos if necessary
+ shadowedLights = viewarea.getShadowedLights();
+ n = shadowedLights.length;
+
+ for (i=0; i<n; i++) {
+ size = shadowedLights[i]._vf.shadowMapSize;
+
+ if (!x3dom.isa(shadowedLights[i], x3dom.nodeTypes.PointLight))
+ //cascades for directional lights
+ numShadowMaps = Math.max(1,Math.min(shadowedLights[i]._vf.shadowCascades,6));
+ else
+ //six maps for point lights
+ numShadowMaps = 6;
+
+ if (typeof scene._webgl.fboShadow[i] === "undefined" ||
+ scene._webgl.fboShadow[i].length != numShadowMaps ||
+ scene._webgl.fboShadow[i][0].height != size) {
+ scene._webgl.fboShadow[i] = [];
+ for (j=0;j<numShadowMaps;j++){
+ scene._webgl.fboShadow[i][j] = x3dom.Utils.initFBO(gl, size, size, shadowType, false, true);
+ }
+ }
+ }
+
+ //reinitialize blur fbos if necessary
+ for (i=0; i<n; i++){
+ size = scene._webgl.fboShadow[i][0].height;
+
+ sizeAvailable = false;
+ for (j = 0; j < scene._webgl.fboBlur.length; j++){
+ if (size == scene._webgl.fboBlur[j].height)
+ sizeAvailable = true;
+ }
+ if (!sizeAvailable)
+ scene._webgl.fboBlur[scene._webgl.fboBlur.length] = x3dom.Utils.initFBO(gl, size, size, shadowType, false, true);
+ }
+
+ if ((x3dom.SSAO.isEnabled(scene) ||scene._webgl.fboShadow.length > 0) && typeof scene._webgl.fboScene == "undefined" || scene._webgl.fboScene &&
+ (this.canvas.width != scene._webgl.fboScene.width || this.canvas.height != scene._webgl.fboScene.height)) {
+ scene._webgl.fboScene = x3dom.Utils.initFBO(gl, this.canvas.width, this.canvas.height, shadowType, false, true);
+ }
+ }
+
+ var env = scene.getEnvironment();
+ // update internal flags
+ env.checkSanity();
+
+ var bgnd = scene.getBackground();
+ // setup or update bgnd
+ this.setupScene(gl, bgnd);
+
+ this.numFaces = 0;
+ this.numCoords = 0;
+ this.numDrawCalls = 0;
+
+ var mat_proj = viewarea.getProjectionMatrix();
+ var mat_view = viewarea.getViewMatrix();
+
+ // fire viewpointChanged event
+ if (!this._calledViewpointChangedHandler || !viewarea._last_mat_view.equals(mat_view)) {
+ var e_viewpoint = scene.getViewpoint();
+ var e_eventType = "viewpointChanged";
+
+ try {
+ if ( e_viewpoint._xmlNode &&
+ (e_viewpoint._xmlNode["on" + e_eventType] ||
+ e_viewpoint._xmlNode.hasAttribute("on" + e_eventType) ||
+ e_viewpoint._listeners[e_eventType]) ) {
+ var e_viewtrafo = e_viewpoint.getCurrentTransform();
+ e_viewtrafo = e_viewtrafo.inverse().mult(mat_view);
+ var e_mat = e_viewtrafo.inverse();
+
+ var e_rotation = new x3dom.fields.Quaternion(0, 0, 1, 0);
+ e_rotation.setValue(e_mat);
+ var e_translation = e_mat.e3();
+
+ var e_event = {
+ target: e_viewpoint._xmlNode,
+ type: e_eventType,
+ matrix: e_viewtrafo,
+ position: e_translation,
+ orientation: e_rotation.toAxisAngle(),
+ cancelBubble: false,
+ stopPropagation: function () { this.cancelBubble = true; },
+ preventDefault: function () { this.cancelBubble = true; }
+ };
+
+ e_viewpoint.callEvtHandler(("on" + e_eventType), e_event);
+
+ this._calledViewpointChangedHandler = true;
+ }
+ }
+ catch (e_e) {
+ x3dom.debug.logException(e_e);
+ }
+ }
+
+ viewarea._last_mat_view = mat_view;
+ viewarea._last_mat_proj = mat_proj;
+
+ var mat_scene = mat_proj.mult(mat_view); //viewarea.getWCtoCCMatrix();
+ viewarea._last_mat_scene = mat_scene;
+
+
+ //===========================================================================
+ // Collect drawables (traverse)
+ //===========================================================================
+ scene.drawableCollection = null; // Always update needed?
+
+ if (!scene.drawableCollection)
+ {
+ var drawableCollectionConfig = {
+ viewArea: viewarea,
+ sortTrans: env._vf.sortTrans,
+ viewMatrix: mat_view,
+ projMatrix: mat_proj,
+ sceneMatrix: mat_scene,
+ frustumCulling: true,
+ smallFeatureThreshold: env._smallFeatureThreshold,
+ context: this,
+ gl: gl
+ };
+
+ scene.drawableCollection = new x3dom.DrawableCollection(drawableCollectionConfig);
+
+ x3dom.Utils.startMeasure('traverse');
+
+ scene.collectDrawableObjects(x3dom.fields.SFMatrix4f.identity(), scene.drawableCollection, true, false, 0, []);
+
+ var traverseTime = x3dom.Utils.stopMeasure('traverse');
+ this.x3dElem.runtime.addMeasurement('TRAVERSE', traverseTime);
+ }
+
+ //===========================================================================
+ // Sort drawables
+ //===========================================================================
+ x3dom.Utils.startMeasure('sorting');
+
+ scene.drawableCollection.sort();
+
+ var sortTime = x3dom.Utils.stopMeasure('sorting');
+ this.x3dElem.runtime.addMeasurement('SORT', sortTime);
+
+ //===========================================================================
+ // Render Shadow Pass
+ //===========================================================================
+ var slights = viewarea.getLights();
+ var numLights = slights.length;
+ var mat_light;
+ var WCToLCMatrices = [];
+ var lMatrices = [];
+ var shadowCount = 0;
+
+ x3dom.Utils.startMeasure('shadow');
+
+ for (var p = 0; p < numLights; p++) {
+ if (slights[p]._vf.shadowIntensity > 0.0) {
+
+ var lightMatrix = viewarea.getLightMatrix()[p];
+ shadowMaps = scene._webgl.fboShadow[shadowCount];
+ var offset = Math.max(0.0, Math.min(1.0, slights[p]._vf.shadowOffset));
+
+ if (!x3dom.isa(slights[p], x3dom.nodeTypes.PointLight)) {
+ //get cascade count
+ var numCascades = Math.max(1, Math.min(slights[p]._vf.shadowCascades, 6));
+
+ //calculate transformation matrices
+ mat_light = viewarea.getWCtoLCMatricesCascaded(lightMatrix, slights[p], mat_proj);
+
+ //render shadow pass
+ for (i = 0; i < numCascades; i++) {
+ this.renderShadowPass(gl, viewarea, mat_light[i], mat_view, shadowMaps[i], offset, false);
+ }
+ }
+ else {
+ //for point lights 6 render passes
+ mat_light = viewarea.getWCtoLCMatricesPointLight(lightMatrix, slights[p], mat_proj);
+ for (i = 0; i < 6; i++) {
+ this.renderShadowPass(gl, viewarea, mat_light[i], mat_view, shadowMaps[i], offset, false);
+ }
+ }
+ shadowCount++;
+
+ //save transformations for shadow rendering
+ WCToLCMatrices[WCToLCMatrices.length] = mat_light;
+ lMatrices[lMatrices.length] = lightMatrix;
+ }
+ }
+
+ //One pass for depth of scene from camera view (to enable post-processing shading)
+ if (shadowCount > 0 || x3dom.SSAO.isEnabled(scene)) {
+ this.renderShadowPass(gl, viewarea, mat_scene, mat_view, scene._webgl.fboScene, 0.0, true);
+ var shadowTime = x3dom.Utils.stopMeasure('shadow');
+ this.x3dElem.runtime.addMeasurement('SHADOW', shadowTime);
+ }
+ else {
+ this.x3dElem.runtime.removeMeasurement('SHADOW');
+ }
+
+ mat_light = viewarea.getWCtoLCMatrix(viewarea.getLightMatrix()[0]);
+
+ for (rtl_i = 0; rtl_i < rtl_n; rtl_i++) {
+ this.renderRTPass(gl, viewarea, rentex[rtl_i]);
+ }
+
+ // rendering
+ x3dom.Utils.startMeasure('render');
+
+ this.stateManager.viewport(0, 0, this.canvas.width, this.canvas.height);
+
+ // calls gl.clear etc. (bgnd stuff)
+ bgnd._webgl.render(gl, mat_view, mat_proj);
+
+ x3dom.nodeTypes.PopGeometry.numRenderedVerts = 0;
+ x3dom.nodeTypes.PopGeometry.numRenderedTris = 0;
+
+ n = scene.drawableCollection.length;
+
+ // Very, very experimental priority culling, currently coupled with frustum and small feature culling
+ // TODO; what about shadows?
+ if (env._vf.smallFeatureCulling && env._lowPriorityThreshold < 1 && viewarea.isMovingOrAnimating()) {
+ n = Math.floor(n * env._lowPriorityThreshold);
+ if (!n && scene.drawableCollection.length)
+ n = 1; // render at least one object
+ }
+
+ this.stateManager.unsetProgram();
+
+ // render all remaining shapes
+ for (i = 0; i < n; i++) {
+ var drawable = scene.drawableCollection.get(i);
+
+ this.renderShape(drawable, viewarea, slights, numLights, mat_view, mat_scene, mat_light, mat_proj, gl);
+ }
+
+ if (shadowCount > 0)
+ this.renderShadows(gl, viewarea, shadowedLights, WCToLCMatrices, lMatrices, mat_view, mat_proj, mat_scene);
+
+ this.stateManager.disable(gl.BLEND);
+ this.stateManager.disable(gl.DEPTH_TEST);
+
+ viewarea._numRenderedNodes = n;
+
+ if(x3dom.SSAO.isEnabled(scene))
+ x3dom.SSAO.renderSSAO(this.stateManager, gl, scene, this.canvas);
+
+ // if _visDbgBuf then show helper buffers in foreground for debugging
+ if (viewarea._visDbgBuf !== undefined && viewarea._visDbgBuf)
+ {
+ var pm = scene._vf.pickMode.toLowerCase();
+
+ if (pm.indexOf("idbuf") == 0 || pm == "color" || pm == "texcoord") {
+ this.stateManager.viewport(0, 3 * this.canvas.height / 4,
+ this.canvas.width / 4, this.canvas.height / 4);
+ scene._fgnd._webgl.render(gl, scene._webgl.fboPick.tex);
+ }
+
+ if (shadowCount > 0 || x3dom.SSAO.isEnabled(scene)) {
+ this.stateManager.viewport(this.canvas.width / 4, 3 * this.canvas.height / 4,
+ this.canvas.width / 4, this.canvas.height / 4);
+ scene._fgnd._webgl.render(gl, scene._webgl.fboScene.tex);
+ }
+
+ var row = 3, col = 2;
+ for (i = 0; i < shadowCount; i++) {
+ var shadowMaps = scene._webgl.fboShadow[i];
+ for (j = 0; j < shadowMaps.length; j++) {
+ this.stateManager.viewport(col * this.canvas.width / 4, row * this.canvas.height / 4,
+ this.canvas.width / 4, this.canvas.height / 4);
+ scene._fgnd._webgl.render(gl, shadowMaps[j].tex);
+ if (col < 2) {
+ col++;
+ } else {
+ col = 0;
+ row--;
+ }
+ }
+ }
+
+ for (rtl_i = 0; rtl_i < rtl_n; rtl_i++) {
+ rt_tex = rentex[rtl_i];
+ if (!rt_tex._webgl.fbo.fbo) // might be deleted (--> RefinementTexture when finished)
+ continue;
+
+ this.stateManager.viewport(rtl_i * this.canvas.width / 8, 5 * this.canvas.height / 8,
+ this.canvas.width / 8, this.canvas.height / 8);
+ scene._fgnd._webgl.render(gl, rt_tex._webgl.fbo.tex);
+ }
+ }
+
+ gl.finish();
+ //gl.flush();
+
+ var renderTime = x3dom.Utils.stopMeasure('render');
+
+ this.x3dElem.runtime.addMeasurement('RENDER', renderTime);
+ this.x3dElem.runtime.addMeasurement('DRAW', (n ? renderTime / n : 0));
+
+ this.x3dElem.runtime.addInfo('#NODES:', scene.drawableCollection.numberOfNodes);
+ this.x3dElem.runtime.addInfo('#SHAPES:', viewarea._numRenderedNodes);
+ this.x3dElem.runtime.addInfo("#DRAWS:", this.numDrawCalls);
+ this.x3dElem.runtime.addInfo("#POINTS:", this.numCoords);
+ this.x3dElem.runtime.addInfo("#TRIS:", this.numFaces);
+
+ //scene.drawableObjects = null;
+ };
+
+ /*****************************************************************************
+ * Render special PingPong-Pass
+ *****************************************************************************/
+ Context.prototype.renderPingPongPass = function (gl, viewarea, rt) {
+ var scene = viewarea._scene;
+ var refinementPos = rt._vf.dimensions[0] + "x" + rt._vf.dimensions[1];
+ var refinementFbo = scene._webgl.refinement[refinementPos];
+
+ // load stamp textures
+ if (rt._currLoadLevel == 0 && (!scene._webgl.refinement.stamps[0] || !scene._webgl.refinement.stamps[1])) {
+ scene._webgl.refinement.stamps[0] = this.cache.getTexture2D(gl, rt._nameSpace.doc,
+ rt._nameSpace.getURL(rt._vf.stamp0), false, false, false, false);
+ scene._webgl.refinement.stamps[1] = this.cache.getTexture2D(gl, rt._nameSpace.doc,
+ rt._nameSpace.getURL(rt._vf.stamp1), false, false, false, false);
+ }
+
+ // load next level of image
+ if (rt._currLoadLevel < rt._loadLevel) {
+ rt._currLoadLevel++;
+
+ if (rt._webgl.texture)
+ gl.deleteTexture(rt._webgl.texture);
+
+ var filename = rt._vf.url[0] + "/" + rt._currLoadLevel + "." + rt._vf.format;
+
+ rt._webgl.texture = x3dom.Utils.createTexture2D(gl, rt._nameSpace.doc,
+ rt._nameSpace.getURL(filename), false, false, false, false);
+
+ if (rt._vf.iterations % 2 === 0)
+ (rt._currLoadLevel % 2 !== 0) ? rt._repeat.x *= 2.0 : rt._repeat.y *= 2.0;
+ else
+ (rt._currLoadLevel % 2 === 0) ? rt._repeat.x *= 2.0 : rt._repeat.y *= 2.0;
+ }
+
+ if (!rt._webgl.texture.ready ||
+ !scene._webgl.refinement.stamps[0].ready || !scene._webgl.refinement.stamps[1].ready)
+ return;
+
+ // first pass
+ this.stateManager.bindFramebuffer(gl.FRAMEBUFFER, refinementFbo.fbo);
+ this.stateManager.viewport(0, 0, refinementFbo.width, refinementFbo.height);
+
+ this.stateManager.disable(gl.BLEND);
+ this.stateManager.disable(gl.CULL_FACE);
+ this.stateManager.disable(gl.DEPTH_TEST);
+
+ gl.clearColor(0, 0, 0, 1);
+ gl.clearDepth(1);
+ gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
+
+ var sp = this.cache.getShader(gl, x3dom.shader.TEXTURE_REFINEMENT);
+ this.stateManager.useProgram(sp);
+
+ gl.bindBuffer(gl.ARRAY_BUFFER, scene._webgl.refinement.positionBuffer);
+ gl.vertexAttribPointer(sp.position, 2, gl.FLOAT, false, 0, 0);
+ gl.enableVertexAttribArray(sp.position);
+
+ sp.stamp = 0;
+ gl.activeTexture(gl.TEXTURE0);
+ gl.bindTexture(gl.TEXTURE_2D, scene._webgl.refinement.stamps[(rt._currLoadLevel + 1) % 2]); // draw stamp
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT);
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT);
+
+ if (rt._currLoadLevel > 1) {
+ sp.lastTex = 1;
+ gl.activeTexture(gl.TEXTURE1);
+ gl.bindTexture(gl.TEXTURE_2D, rt._webgl.fbo.tex);
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
+ 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);
+ }
+
+ sp.curTex = 2;
+ gl.activeTexture(gl.TEXTURE2);
+ gl.bindTexture(gl.TEXTURE_2D, rt._webgl.texture); // draw level image to fbo
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
+ 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);
+
+ sp.mode = rt._currLoadLevel - 1;
+ sp.repeat = rt._repeat.toGL();
+
+ gl.drawArrays(gl.TRIANGLES, 0, 6);
+
+ // second pass
+ this.stateManager.bindFramebuffer(gl.FRAMEBUFFER, rt._webgl.fbo.fbo);
+ gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
+
+ sp.mode = 0;
+ sp.curTex = 2;
+ gl.activeTexture(gl.TEXTURE2);
+ gl.bindTexture(gl.TEXTURE_2D, refinementFbo.tex); // draw result to fbo
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
+ 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.drawArrays(gl.TRIANGLES, 0, 6);
+
+ gl.activeTexture(gl.TEXTURE0);
+ gl.bindTexture(gl.TEXTURE_2D, null);
+
+ gl.disableVertexAttribArray(sp.position);
+
+ // pass done
+ this.stateManager.bindFramebuffer(gl.FRAMEBUFFER, null);
+ this.stateManager.viewport(0, 0, this.canvas.width, this.canvas.height);
+
+ if (rt._vf.autoRefinement)
+ rt.nextLevel();
+
+ if (rt._currLoadLevel == rt._vf.maxLevel)
+ rt._currLoadLevel++;
+
+ if (rt._webgl.fbo.mipMap) {
+ gl.bindTexture(gl.TEXTURE_2D, rt._webgl.fbo.tex);
+ gl.generateMipmap(gl.TEXTURE_2D);
+ gl.bindTexture(gl.TEXTURE_2D, null);
+ }
+
+ // we're finally done: cleanup/delete all helper FBOs
+ if (!rt.requirePingPong()) {
+ gl.deleteTexture(rt._webgl.texture);
+ delete rt._webgl.texture;
+
+ rt._cleanupGLObjects(true);
+ }
+
+ rt._renderedImage++;
+ };
+
+ /*****************************************************************************
+ * Render RenderedTexture-Pass
+ *****************************************************************************/
+ Context.prototype.renderRTPass = function (gl, viewarea, rt)
+ {
+ /// begin special case (progressive image refinement)
+ if (x3dom.isa(rt, x3dom.nodeTypes.RefinementTexture)) {
+ if (rt.requirePingPong()) {
+ this.renderPingPongPass(gl, viewarea, rt);
+ }
+ return;
+ }
+ /// end special case
+
+ switch (rt._vf.update.toUpperCase()) {
+ case "NONE":
+ return;
+ case "NEXT_FRAME_ONLY":
+ if (!rt._needRenderUpdate) {
+ return;
+ }
+ rt._needRenderUpdate = false;
+ break;
+ case "ALWAYS":
+ default:
+ break;
+ }
+
+ var scene = viewarea._scene;
+ var bgnd = null;
+
+ var mat_view = rt.getViewMatrix();
+ var mat_proj = rt.getProjectionMatrix();
+ var mat_scene = mat_proj.mult(mat_view);
+
+ var lightMatrix = viewarea.getLightMatrix()[0];
+ var mat_light = viewarea.getWCtoLCMatrix(lightMatrix);
+
+ var i, n, m = rt._cf.excludeNodes.nodes.length;
+
+ var arr = new Array(m);
+ for (i = 0; i < m; i++) {
+ var render = rt._cf.excludeNodes.nodes[i]._vf.render;
+ if (render === undefined) {
+ arr[i] = -1;
+ }
+ else {
+ if (render === true) {
+ arr[i] = 1;
+ } else {
+ arr[i] = 0;
+ }
+ }
+ rt._cf.excludeNodes.nodes[i]._vf.render = false;
+ }
+
+ this.stateManager.bindFramebuffer(gl.FRAMEBUFFER, rt._webgl.fbo.fbo);
+
+ this.stateManager.viewport(0, 0, rt._webgl.fbo.width, rt._webgl.fbo.height);
+
+ if (rt._cf.background.node === null) {
+ gl.clearColor(0, 0, 0, 1);
+ gl.clearDepth(1.0);
+ gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT | gl.STENCIL_BUFFER_BIT);
+ }
+ else if (rt._cf.background.node === scene.getBackground()) {
+ bgnd = scene.getBackground();
+ bgnd._webgl.render(gl, mat_view, mat_proj);
+ }
+ else {
+ bgnd = rt._cf.background.node;
+ this.setupScene(gl, bgnd);
+ bgnd._webgl.render(gl, mat_view, mat_proj);
+ }
+
+ this.stateManager.depthFunc(gl.LEQUAL);
+ this.stateManager.enable(gl.DEPTH_TEST);
+ this.stateManager.enable(gl.CULL_FACE);
+
+ this.stateManager.blendFuncSeparate(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE);
+ this.stateManager.enable(gl.BLEND);
+
+ var slights = viewarea.getLights();
+ var numLights = slights.length;
+
+ var transform, shape, drawable;
+ var locScene = rt._cf.scene.node;
+
+ if (!locScene || locScene === scene) {
+ n = scene.drawableCollection.length;
+
+ if (rt._vf.showNormals) {
+ this.renderNormals(gl, scene, scene._webgl.normalShader, mat_view, mat_scene);
+ }
+ else {
+ this.stateManager.unsetProgram();
+
+ for (i = 0; i < n; i++) {
+ drawable = scene.drawableCollection.get(i);
+
+ this.renderShape(drawable, viewarea, slights, numLights,
+ mat_view, mat_scene, mat_light, mat_proj, gl);
+ }
+ }
+ }
+ else {
+ var env = scene.getEnvironment();
+
+ var drawableCollectionConfig = {
+ viewArea: viewarea,
+ sortTrans: env._vf.sortTrans,
+ viewMatrix: mat_view,
+ projMatrix: mat_proj,
+ sceneMatrix: mat_scene,
+ frustumCulling: false,
+ smallFeatureThreshold: 1,
+ context: this,
+ gl: gl
+ };
+
+ locScene.numberOfNodes = 0;
+ locScene.drawableCollection = new x3dom.DrawableCollection(drawableCollectionConfig);
+
+ locScene.collectDrawableObjects(x3dom.fields.SFMatrix4f.identity(),
+ locScene.drawableCollection, true, false, 0, []);
+
+ locScene.drawableCollection.sort();
+
+ n = locScene.drawableCollection.length;
+
+ if (rt._vf.showNormals) {
+ this.renderNormals(gl, locScene, scene._webgl.normalShader, mat_view, mat_scene);
+ }
+ else {
+ this.stateManager.unsetProgram();
+
+ for (i = 0; i < n; i++) {
+ drawable = locScene.drawableCollection.get(i);
+
+ if (!drawable.shape._vf.render) {
+ continue;
+ }
+
+ this.renderShape(drawable, viewarea, slights, numLights,
+ mat_view, mat_scene, mat_light, mat_proj, gl);
+ }
+ }
+ }
+
+ this.stateManager.disable(gl.BLEND);
+ this.stateManager.disable(gl.DEPTH_TEST);
+
+ gl.flush();
+ this.stateManager.bindFramebuffer(gl.FRAMEBUFFER, null);
+
+ if (rt._webgl.fbo.mipMap) {
+ gl.bindTexture(gl.TEXTURE_2D, rt._webgl.fbo.tex);
+ gl.generateMipmap(gl.TEXTURE_2D);
+ gl.bindTexture(gl.TEXTURE_2D, null);
+ }
+
+ for (i = 0; i < m; i++) {
+ if (arr[i] !== 0) {
+ rt._cf.excludeNodes.nodes[i]._vf.render = true;
+ }
+ }
+ };
+
+ /*****************************************************************************
+ * Render Normals
+ *****************************************************************************/
+ Context.prototype.renderNormals = function (gl, scene, sp, mat_view, mat_scene)
+ {
+ if (!sp || !scene) { // error
+ return;
+ }
+
+ this.stateManager.depthFunc(gl.LEQUAL);
+ this.stateManager.enable(gl.DEPTH_TEST);
+ this.stateManager.enable(gl.CULL_FACE);
+ this.stateManager.disable(gl.BLEND);
+
+ this.stateManager.useProgram(sp);
+
+ var bgCenter = x3dom.fields.SFVec3f.NullVector.toGL();
+ var bgSize = x3dom.fields.SFVec3f.OneVector.toGL();
+
+ for (var i = 0, n = scene.drawableCollection.length; i < n; i++)
+ {
+ var drawable = scene.drawableCollection.get(i);
+ var trafo = drawable.transform;
+ var shape = drawable.shape;
+ var s_gl = shape._webgl;
+
+ if (!s_gl || !shape || !shape._vf.render) {
+ continue;
+ }
+
+ var s_geo = shape._cf.geometry.node;
+ var s_msh = s_geo._mesh;
+
+ var model_view_inv = mat_view.mult(trafo).inverse();
+ sp.normalMatrix = model_view_inv.transpose().toGL();
+ sp.modelViewProjectionMatrix = mat_scene.mult(trafo).toGL();
+
+ //Set ImageGeometry switch (TODO; also impl. in Shader!)
+ sp.imageGeometry = s_gl.imageGeometry;
+
+ if (s_gl.coordType != gl.FLOAT) {
+ if (s_gl.popGeometry != 0 ||
+ (s_msh._numPosComponents == 4 && x3dom.Utils.isUnsignedType(s_geo._vf.coordType)))
+ sp.bgCenter = s_geo.getMin().toGL();
+ else
+ sp.bgCenter = s_geo._vf.position.toGL();
+ sp.bgSize = s_geo._vf.size.toGL();
+ sp.bgPrecisionMax = s_geo.getPrecisionMax('coordType');
+ }
+ else {
+ sp.bgCenter = bgCenter;
+ sp.bgSize = bgSize;
+ sp.bgPrecisionMax = 1;
+ }
+ if (s_gl.normalType != gl.FLOAT) {
+ sp.bgPrecisionNorMax = s_geo.getPrecisionMax('normalType');
+ }
+ else {
+ sp.bgPrecisionNorMax = 1;
+ }
+
+ if (shape.isSolid()) {
+ this.stateManager.enable(gl.CULL_FACE);
+
+ if (shape.isCCW()) {
+ this.stateManager.frontFace(gl.CCW);
+ }
+ else {
+ this.stateManager.frontFace(gl.CW);
+ }
+ }
+ else {
+ this.stateManager.disable(gl.CULL_FACE);
+ }
+
+
+ // render shape
+ for (var q = 0, q_n = s_gl.positions.length; q < q_n; q++) {
+ var q6 = 6 * q;
+ var v, v_n, offset;
+
+ if ( !(sp.position !== undefined && s_gl.buffers[q6 + 1] && s_gl.indexes[q]) )
+ continue;
+
+ // bind buffers
+ if (s_gl.buffers[q6]) {
+ gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, s_gl.buffers[q6]);
+ }
+
+ gl.bindBuffer(gl.ARRAY_BUFFER, s_gl.buffers[q6 + 1]);
+
+ gl.vertexAttribPointer(sp.position,
+ s_msh._numPosComponents, s_gl.coordType, false,
+ shape._coordStrideOffset[0], shape._coordStrideOffset[1]);
+ gl.enableVertexAttribArray(sp.position);
+
+ if (sp.normal !== undefined && s_gl.buffers[q6 + 2]) {
+ gl.bindBuffer(gl.ARRAY_BUFFER, s_gl.buffers[q6 + 2]);
+
+ gl.vertexAttribPointer(sp.normal,
+ s_msh._numNormComponents, s_gl.normalType, false,
+ shape._normalStrideOffset[0], shape._normalStrideOffset[1]);
+ gl.enableVertexAttribArray(sp.normal);
+ }
+
+ // draw mesh
+ if (s_gl.binaryGeometry > 0 || s_gl.popGeometry > 0) {
+ for (v = 0, offset = 0, v_n = s_geo._vf.vertexCount.length; v < v_n; v++) {
+ gl.drawElements(s_gl.primType[v], s_geo._vf.vertexCount[v], s_gl.indexType,
+ x3dom.Utils.getByteAwareOffset(offset, s_gl.indexType, gl));
+ offset += s_geo._vf.vertexCount[v];
+ }
+ }
+ else if (s_gl.binaryGeometry < 0 || s_gl.popGeometry < 0 || s_gl.imageGeometry) {
+ for (v = 0, offset = 0, v_n = s_geo._vf.vertexCount.length; v < v_n; v++) {
+ gl.drawArrays(s_gl.primType[v], offset, s_geo._vf.vertexCount[v]);
+ offset += s_geo._vf.vertexCount[v];
+ }
+ }
+ else if (s_geo.hasIndexOffset()) {
+ var indOff = shape.tessellationProperties();
+ for (v = 0, v_n = indOff.length; v < v_n; v++) {
+ gl.drawElements(s_gl.primType, indOff[v].count, s_gl.indexType,
+ indOff[v].offset * x3dom.Utils.getOffsetMultiplier(s_gl.indexType, gl));
+ }
+ }
+ else if (s_gl.indexes[q].length == 0) {
+ gl.drawArrays(s_gl.primType, 0, s_gl.positions[q].length / 3);
+ }
+ else {
+ gl.drawElements(s_gl.primType, s_gl.indexes[q].length, s_gl.indexType, 0);
+ }
+
+ gl.disableVertexAttribArray(sp.position);
+
+ if (sp.normal !== undefined) {
+ gl.disableVertexAttribArray(sp.normal);
+ }
+ }
+ }
+ };
+
+ /*****************************************************************************
+ * Cleanup
+ *****************************************************************************/
+ Context.prototype.shutdown = function (viewarea) {
+ var gl = this.ctx3d;
+ var scene = viewarea._scene;
+
+ if (gl == null || !scene) {
+ return;
+ }
+
+ var bgnd = scene.getBackground();
+ if (bgnd._webgl.position !== undefined) {
+ gl.deleteBuffer(bgnd._webgl.buffers[1]);
+ gl.deleteBuffer(bgnd._webgl.buffers[0]);
+ }
+ var fgnd = scene._fgnd;
+ if (fgnd._webgl.position !== undefined) {
+ gl.deleteBuffer(fgnd._webgl.buffers[1]);
+ gl.deleteBuffer(fgnd._webgl.buffers[0]);
+ }
+
+ var n = scene.drawableCollection ? scene.drawableCollection.length : 0;
+ for (var i = 0; i < n; i++) {
+ var shape = scene.drawableCollection.get(i).shape;
+
+ if (shape._cleanupGLObjects)
+ shape._cleanupGLObjects(true);
+ }
+
+ //Release Texture and Shader Resources
+ this.cache.Release(gl);
+ };
+
+ /*****************************************************************************
+ * Draw shadows on screen
+ *****************************************************************************/
+ Context.prototype.renderShadows = function(gl, viewarea, shadowedLights, wctolc, lMatrices,
+ mat_view, mat_proj, mat_scene)
+ {
+ var scene = viewarea._scene;
+
+ //don't render shadows with less than 7 textures per fragment shader
+ var texLimit = x3dom.caps.MAX_TEXTURE_IMAGE_UNITS;
+
+ if (texLimit < 7)
+ return;
+
+ var texUnits = 1;
+ var renderSplit = [ 0 ];
+
+ var shadowMaps, numShadowMaps;
+ var i, j, k;
+
+ //filter shadow maps and determine, if multiple render passes are needed
+ for (i = 0; i < shadowedLights.length; i++)
+ {
+ var filterSize = shadowedLights[i]._vf.shadowFilterSize;
+ shadowMaps = scene._webgl.fboShadow[i];
+ numShadowMaps = shadowMaps.length;
+
+ //filtering
+ for (j=0; j<numShadowMaps;j++){
+ this.blurTex(gl, scene, shadowMaps[j], filterSize);
+ }
+
+ //shader consumes 6 tex units per lights (even if less are bound)
+ texUnits+=6;
+
+ if (texUnits > texLimit){
+ renderSplit[renderSplit.length] = i;
+ texUnits = 7;
+ }
+ }
+ renderSplit[renderSplit.length] = shadowedLights.length;
+
+ //render shadows for current render split
+ var n = renderSplit.length - 1;
+ var mat_proj_inv = mat_proj.inverse();
+ var mat_scene_inv = mat_scene.inverse();
+
+ //enable (multiplicative) blending
+ this.stateManager.enable(gl.BLEND);
+ this.stateManager.blendFunc(gl.DST_COLOR, gl.ZERO);
+
+ for (var s=0; s<n; s++)
+ {
+ var startIndex = renderSplit[s];
+ var endIndex = renderSplit[s+1];
+
+ var currentLights = [];
+
+ for (k=startIndex; k<endIndex; k++)
+ currentLights[currentLights.length] = shadowedLights[k];
+
+ var sp = this.cache.getShadowRenderingShader(gl, currentLights);
+
+ this.stateManager.useProgram(sp);
+
+ gl.bindBuffer(gl.ARRAY_BUFFER, scene._webgl.ppBuffer);
+ gl.vertexAttribPointer(sp.position, 2, gl.FLOAT, false, 0, 0);
+ gl.enableVertexAttribArray(sp.position);
+
+ //bind depth texture (depth from camera view)
+ sp.sceneMap = 0;
+ gl.activeTexture(gl.TEXTURE0);
+ gl.bindTexture(gl.TEXTURE_2D, scene._webgl.fboScene.tex);
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
+ 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);
+
+ //compute inverse projection matrix
+ sp.inverseProj = mat_proj_inv.toGL();
+
+ //compute inverse view projection matrix
+ sp.inverseViewProj = mat_scene_inv.toGL();
+
+ var mat_light;
+ var lightMatrix;
+ var shadowIndex = 0;
+
+ for (var p=0, pn=currentLights.length; p<pn; p++) {
+ //get light matrices and shadow maps for current light
+ lightMatrix = lMatrices[p+startIndex];
+ mat_light = wctolc[p+startIndex];
+ shadowMaps = scene._webgl.fboShadow[p+startIndex];
+
+ numShadowMaps = mat_light.length;
+
+ for (i=0; i< numShadowMaps; i++){
+ gl.activeTexture(gl.TEXTURE1 + shadowIndex);
+ gl.bindTexture(gl.TEXTURE_2D, shadowMaps[i].tex);
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
+ 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);
+
+ sp['light'+p+'_'+i+'_ShadowMap'] = shadowIndex+1;
+ sp['light'+p+'_'+i+'_Matrix'] = mat_light[i].toGL();
+ shadowIndex++;
+ }
+ sp['light'+p+'_ViewMatrix'] = lightMatrix.toGL();
+
+ //cascade depths for directional and spot light
+ if (!x3dom.isa(currentLights[p], x3dom.nodeTypes.PointLight)){
+ for (j=0; j< numShadowMaps; j++){
+ var numCascades = Math.max(1,Math.min(currentLights[p]._vf.shadowCascades,6));
+ var splitFactor = Math.max(0,Math.min(currentLights[p]._vf.shadowSplitFactor,1));
+ var splitOffset = Math.max(0,Math.min(currentLights[p]._vf.shadowSplitOffset,1));
+
+ var splitDepths = viewarea.getShadowSplitDepths(numCascades, splitFactor, splitOffset, false, mat_proj);
+ sp['light'+p+'_'+j+'_Split'] = splitDepths[j+1];
+ }
+ }
+
+ //assign light properties
+ var light_transform = mat_view.mult(currentLights[p].getCurrentTransform());
+ if(x3dom.isa(currentLights[p], x3dom.nodeTypes.DirectionalLight))
+ {
+ sp['light'+p+'_Type'] = 0.0;
+ sp['light'+p+'_On'] = (currentLights[p]._vf.on) ? 1.0 : 0.0;
+ sp['light'+p+'_Direction'] = light_transform.multMatrixVec(currentLights[p]._vf.direction).toGL();
+ sp['light'+p+'_Attenuation'] = [1.0, 1.0, 1.0];
+ sp['light'+p+'_Location'] = [1.0, 1.0, 1.0];
+ sp['light'+p+'_Radius'] = 0.0;
+ sp['light'+p+'_BeamWidth'] = 0.0;
+ sp['light'+p+'_CutOffAngle'] = 0.0;
+ sp['light'+p+'_ShadowIntensity'] = currentLights[p]._vf.shadowIntensity;
+ sp['light'+p+'_ShadowCascades'] = currentLights[p]._vf.shadowCascades;
+ sp['light'+p+'_ShadowOffset'] = Math.max(0.0,Math.min(1.0,currentLights[p]._vf.shadowOffset));
+ }
+ else if(x3dom.isa(currentLights[p], x3dom.nodeTypes.PointLight))
+ {
+ sp['light'+p+'_Type'] = 1.0;
+ sp['light'+p+'_On'] = (currentLights[p]._vf.on) ? 1.0 : 0.0;
+ sp['light'+p+'_Direction'] = [1.0, 1.0, 1.0];
+ sp['light'+p+'_Attenuation'] = currentLights[p]._vf.attenuation.toGL();
+ sp['light'+p+'_Location'] = light_transform.multMatrixPnt(currentLights[p]._vf.location).toGL();
+ sp['light'+p+'_Radius'] = currentLights[p]._vf.radius;
+ sp['light'+p+'_BeamWidth'] = 0.0;
+ sp['light'+p+'_CutOffAngle'] = 0.0;
+ sp['light'+p+'_ShadowIntensity'] = currentLights[p]._vf.shadowIntensity;
+ sp['light'+p+'_ShadowOffset'] = Math.max(0.0,Math.min(1.0,currentLights[p]._vf.shadowOffset));
+ }
+ else if(x3dom.isa(currentLights[p], x3dom.nodeTypes.SpotLight))
+ {
+ sp['light'+p+'_Type'] = 2.0;
+ sp['light'+p+'_On'] = (currentLights[p]._vf.on) ? 1.0 : 0.0;
+ sp['light'+p+'_Direction'] = light_transform.multMatrixVec(currentLights[p]._vf.direction).toGL();
+ sp['light'+p+'_Attenuation'] = currentLights[p]._vf.attenuation.toGL();
+ sp['light'+p+'_Location'] = light_transform.multMatrixPnt(currentLights[p]._vf.location).toGL();
+ sp['light'+p+'_Radius'] = currentLights[p]._vf.radius;
+ sp['light'+p+'_BeamWidth'] = currentLights[p]._vf.beamWidth;
+ sp['light'+p+'_CutOffAngle'] = currentLights[p]._vf.cutOffAngle;
+ sp['light'+p+'_ShadowIntensity'] = currentLights[p]._vf.shadowIntensity;
+ sp['light'+p+'_ShadowCascades'] = currentLights[p]._vf.shadowCascades;
+ sp['light'+p+'_ShadowOffset'] = Math.max(0.0,Math.min(1.0,currentLights[p]._vf.shadowOffset));
+ }
+ }
+
+ gl.drawArrays(gl.TRIANGLES,0,6);
+
+ //cleanup
+ var nk = shadowIndex + 1;
+ for (k=0; k<nk; k++) {
+ gl.activeTexture(gl.TEXTURE0 + k);
+ gl.bindTexture(gl.TEXTURE_2D, null);
+ }
+ gl.disableVertexAttribArray(sp.position);
+ }
+
+ this.stateManager.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
+ };
+
+ /*****************************************************************************
+ * Blur texture associated with given fbo
+ *****************************************************************************/
+ Context.prototype.blurTex = function(gl, scene, targetFbo, filterSize)
+ {
+ if (filterSize <= 0)
+ return;
+ else if (filterSize < 5)
+ filterSize = 3;
+ else if (filterSize < 7)
+ filterSize = 5;
+ else
+ filterSize = 7;
+
+ //first pass (horizontal blur), result stored in fboBlur
+ var width = targetFbo.width;
+ var height = targetFbo.height;
+ var fboBlur = null;
+
+ for (var i=0, n=scene._webgl.fboBlur.length; i<n; i++)
+ if (height == scene._webgl.fboBlur[i].height) {
+ fboBlur = scene._webgl.fboBlur[i];
+ break; // THINKABOUTME
+ }
+
+ this.stateManager.bindFramebuffer(gl.FRAMEBUFFER, fboBlur.fbo);
+ this.stateManager.viewport(0, 0, width, height);
+
+ this.stateManager.enable(gl.BLEND);
+ this.stateManager.blendFunc(gl.ONE, gl.ZERO);
+ this.stateManager.disable(gl.CULL_FACE);
+ this.stateManager.disable(gl.DEPTH_TEST);
+
+ gl.clearColor(1.0, 1.0, 1.0, 0.0);
+ gl.clearDepth(1.0);
+ gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
+
+ var sp = this.cache.getShader(gl, x3dom.shader.BLUR);
+
+ this.stateManager.useProgram(sp);
+
+ //initialize Data for post processing
+ gl.bindBuffer(gl.ARRAY_BUFFER, scene._webgl.ppBuffer);
+ gl.vertexAttribPointer(sp.position, 2, gl.FLOAT, false, 0, 0);
+ gl.enableVertexAttribArray(sp.position);
+
+ sp.pixelSizeHor = 1.0/width;
+ sp.pixelSizeVert = 1.0/height;
+ sp.filterSize = filterSize;
+ sp.horizontal = true;
+
+ sp.texture = 0;
+
+ //bind texture
+ gl.activeTexture(gl.TEXTURE0);
+ gl.bindTexture(gl.TEXTURE_2D, targetFbo.tex);
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
+ 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.drawArrays(gl.TRIANGLES,0,6);
+
+ //second pass (vertical blur), result stored in targetFbo
+ this.stateManager.bindFramebuffer(gl.FRAMEBUFFER, targetFbo.fbo);
+
+ gl.clearColor(1.0, 1.0, 1.0, 0.0);
+ gl.clearDepth(1.0);
+ gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
+
+ sp.horizontal = false;
+
+ gl.activeTexture(gl.TEXTURE0);
+ gl.bindTexture(gl.TEXTURE_2D, fboBlur.tex);
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
+ 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.drawArrays(gl.TRIANGLES,0,6);
+
+ //cleanup
+ gl.activeTexture(gl.TEXTURE0);
+ gl.bindTexture(gl.TEXTURE_2D, null);
+ gl.disableVertexAttribArray(sp.position);
+ gl.flush();
+
+ this.stateManager.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
+ this.stateManager.bindFramebuffer(gl.FRAMEBUFFER, null);
+ this.stateManager.viewport(0, 0, this.canvas.width, this.canvas.height);
+ };
+
+ return setupContext;
+
+})();
+
+/*
+ * X3DOM JavaScript Library
+ * http://www.x3dom.org
+ *
+ * (C)2009 Fraunhofer IGD, Darmstadt, Germany
+ * Dual licensed under the MIT and GPL
+ *
+ * Based on code originally provided by
+ * Philip Taylor: http://philip.html5.org
+ */
+
+x3dom.bridge = {
+
+ setFlashReady: function (driver, canvas) {
+ var x3dCanvas = x3dom.canvases[canvas];
+ x3dCanvas.isFlashReady = true;
+ x3dom.debug.logInfo('Flash is ready for rendering (' + driver + ')');
+ },
+
+ onMouseDown: function (x, y, button, canvas) {
+ var x3dCanvas = x3dom.canvases[canvas];
+ x3dCanvas.doc.onMousePress(x3dCanvas.gl, x, y, button);
+ x3dCanvas.doc.needRender = true;
+ },
+
+ onMouseUp: function (x, y, button, canvas) {
+ var x3dCanvas = x3dom.canvases[canvas];
+ x3dCanvas.doc.onMouseRelease(x3dCanvas.gl, x, y, button);
+ x3dCanvas.doc.needRender = true;
+ },
+
+ onMouseOver: function (x, y, button, canvas) {
+ var x3dCanvas = x3dom.canvases[canvas];
+ x3dCanvas.doc.onMouseOver(x3dCanvas.gl, x, y, button);
+ x3dCanvas.doc.needRender = true;
+ },
+
+ onMouseOut: function (x, y, button, canvas) {
+ var x3dCanvas = x3dom.canvases[canvas];
+ x3dCanvas.doc.onMouseOut(x3dCanvas.gl, x, y, button);
+ x3dCanvas.doc.needRender = true;
+ },
+
+ onDoubleClick: function (x, y, canvas) {
+ var x3dCanvas = x3dom.canvases[canvas];
+ x3dCanvas.doc.onDoubleClick(x3dCanvas.gl, x, y);
+ x3dCanvas.doc.needRender = true;
+ x3dom.debug.logInfo("dblClick");
+ },
+
+ onMouseDrag: function (x, y, button, canvas) {
+ var x3dCanvas = x3dom.canvases[canvas];
+ x3dCanvas.doc.onDrag(x3dCanvas.gl, x, y, button);
+ x3dCanvas.doc.needRender = true;
+ },
+
+ onMouseMove: function (x, y, button, canvas) {
+ var x3dCanvas = x3dom.canvases[canvas];
+ x3dCanvas.doc.onMove(x3dCanvas.gl, x, y, button);
+ x3dCanvas.doc.needRender = true;
+ },
+
+ onMouseWheel: function (x, y, button, canvas) {
+ var x3dCanvas = x3dom.canvases[canvas];
+ x3dCanvas.doc.onDrag(x3dCanvas.gl, x, y, button);
+ x3dCanvas.doc.needRender = true;
+ },
+
+ onKeyDown: function (charCode, canvas) {
+ var x3dCanvas = x3dom.canvases[canvas];
+ var keysEnabled = x3dCanvas.x3dElem.getAttribute("keysEnabled");
+ if (!keysEnabled || keysEnabled.toLowerCase() === "true") {
+ x3dCanvas.doc.onKeyPress(charCode);
+ }
+ x3dCanvas.doc.needRender = true;
+ },
+
+ setBBox: function (id, center, size) {
+ var shape = x3dom.nodeTypes.Shape.idMap.nodeID[id];
+ //shape._vf.bboxCenter.setValues( new x3dom.fields.SFVec3f(center.x,center.y,center.z) );
+ //shape._vf.bboxSize.setValues( new x3dom.fields.SFVec3f(size.x,size.y,size.z) );
+ },
+
+ setShapeDirty: function (id) {
+ var shape = x3dom.nodeTypes.Shape.idMap.nodeID[id];
+ shape.setAllDirty();
+ }
+};
+
+
+x3dom.gfx_flash = (function () {
+
+ /** Context
+ *
+ */
+ function Context(object, name, renderType) {
+ this.object = object;
+ this.name = name;
+ this.isAlreadySet = false;
+ this.renderType = renderType;
+ }
+
+ /** setup context
+ *
+ */
+ function setupContext(object, renderType) {
+
+ //Set max indexable coords
+ x3dom.Utils.maxIndexableCoords = 65535;
+
+ //Return new Context
+ return new Context(object, 'flash', renderType);
+ }
+
+ /** get context name
+ *
+ */
+ Context.prototype.getName = function () {
+ return this.name;
+ };
+
+ /** render scene
+ *
+ */
+ Context.prototype.renderScene = function (viewarea) {
+ //Get Scene from Viewarea
+ var scene = viewarea._scene;
+
+ var min = x3dom.fields.SFVec3f.MAX();
+ var max = x3dom.fields.SFVec3f.MIN();
+
+ var vol = scene.getVolume();
+ vol.getBounds(min, max);
+
+ scene._lastMin = min;
+ scene._lastMax = max;
+
+ viewarea._last_mat_view = x3dom.fields.SFMatrix4f.identity();
+ viewarea._last_mat_proj = x3dom.fields.SFMatrix4f.identity();
+ viewarea._last_mat_scene = x3dom.fields.SFMatrix4f.identity();
+
+ //Dirty HACK
+ var viewpoint = scene.getViewpoint();
+ if (viewpoint._vf.zNear == -1 || viewpoint._vf.zFar == -1) {
+ viewpoint._vf.zFar = 20000;
+ viewpoint._vf.zNear = 0.1;
+ }
+
+ var mat_view = viewarea.getViewMatrix();
+ var mat_proj = viewarea.getProjectionMatrix();
+ var mat_scene = mat_proj.mult(mat_view);
+
+ //Setup the flash scene
+ this.setupScene(scene, viewarea);
+
+ //Get background node
+ var background = scene.getBackground();
+
+ //Setup the background
+ this.setupBackground(background);
+
+ // Get the fog node
+ var fog = scene.getFog();
+
+ // Setup the fog
+ this.setupFog(fog);
+
+ //Collect all drawableObjects
+ scene.drawableCollection = null;
+ var env = scene.getEnvironment();
+
+ var drawableCollectionConfig = {
+ viewArea: viewarea,
+ sortTrans: env._vf.sortTrans,
+ viewMatrix: mat_view,
+ projMatrix: mat_proj,
+ sceneMatrix: mat_scene,
+ frustumCulling: false,
+ smallFeatureThreshold: false,
+ context: null,
+ gl: null
+ };
+
+ scene.drawableCollection = new x3dom.DrawableCollection(drawableCollectionConfig);
+ scene.collectDrawableObjects(x3dom.fields.SFMatrix4f.identity(), scene.drawableCollection, true, false, 0, []);
+
+ scene.drawableCollection.concat();
+
+ //Get Number of drawableObjects
+ var numDrawableObjects = scene.drawableCollection.length;
+
+ if (numDrawableObjects > 0) {
+ var RefList = [];
+
+ //Iterate over all Objects for setup
+ for (var i = 0; i < numDrawableObjects; i++) {
+ //Get object and transformation
+ var drawable = scene.drawableCollection.get(i);
+ var trafo = drawable.transform;
+ var obj3d = drawable.shape;
+
+ //Count shape references for DEF/USE
+ if (RefList[obj3d._objectID] != undefined) {
+ RefList[obj3d._objectID]++;
+ } else {
+ RefList[obj3d._objectID] = 0;
+ }
+
+ // TODO; move to addDrawable()
+ this.setupShape(obj3d, trafo, RefList[obj3d._objectID]);
+ }
+ }
+
+ //Render the flash scene
+ this.object.renderScene();
+ };
+
+ /** setup scene
+ *
+ */
+ Context.prototype.setupScene = function (scene, viewarea) {
+
+ //Set View-Matrix
+ var mat_view = viewarea.getViewMatrix();
+
+ // fire viewpointChanged event
+ if (!viewarea._last_mat_view.equals(mat_view)) {
+ var e_viewpoint = viewarea._scene.getViewpoint();
+ var e_eventType = "viewpointChanged";
+ /*TEST*/
+ try {
+ if (e_viewpoint._xmlNode &&
+ (e_viewpoint._xmlNode["on" + e_eventType] ||
+ e_viewpoint._xmlNode.hasAttribute("on" + e_eventType) ||
+ e_viewpoint._listeners[e_eventType])) {
+ var e_viewtrafo = e_viewpoint.getCurrentTransform();
+ e_viewtrafo = e_viewtrafo.inverse().mult(mat_view);
+
+ var e_mat = e_viewtrafo.inverse();
+
+ var e_rotation = new x3dom.fields.Quaternion(0, 0, 1, 0);
+ //e_rotation.setValue(e_mat);
+
+ var e_translation = e_mat.e3();
+
+ var e_event = {
+ target: e_viewpoint._xmlNode,
+ type: e_eventType,
+ matrix: e_viewtrafo,
+ position: e_translation,
+ orientation: e_rotation.toAxisAngle(),
+ cancelBubble: false,
+ stopPropagation: function () {
+ this.cancelBubble = true;
+ }
+ };
+
+ e_viewpoint.callEvtHandler(e_eventType, e_event);
+ }
+ }
+ catch (e_e) {
+ x3dom.debug.logException(e_e);
+ }
+ }
+
+ viewarea._last_mat_view = mat_view;
+
+ //Dirty HACK
+ var viewpoint = scene.getViewpoint();
+ //viewpoint._vf.zFar = 100;
+ //viewpoint._vf.zNear = 0.1;
+
+ var mat_proj = viewarea.getProjectionMatrix();
+
+ this.object.setViewpoint({ fov: viewpoint._vf.fov,
+ zFar: viewpoint._vf.zFar,
+ zNear: viewpoint._vf.zNear,
+ viewMatrix: mat_view.toGL(),
+ projectionMatrix: mat_proj.toGL() });
+
+ //Set HeadLight
+ var nav = scene.getNavigationInfo();
+ if (nav._vf.headlight) {
+ /*this.object.setLights( { idx: 0,
+ type: 0,
+ on: 1.0,
+ color: [1.0, 1.0, 1.0],
+ intensity: 1.0,
+ ambientIntensity: 0.0,
+ direction: [0.0, 0.0, 1.0],
+ attenuation: [1.0, 1.0, 1.0],
+ location: [1.0, 1.0, 1.0],
+ radius: 0.0,
+ beamWidth: 0.0,
+ cutOffAngle: 0.0 } );*/
+
+ this.object.setHeadLight({ id: -1,
+ on: 1.0,
+ color: [1.0, 1.0, 1.0],
+ intensity: 1.0,
+ ambientIntensity: 0.0,
+ direction: [0.0, 0.0, -1.0] });
+ }
+
+ //TODO Set Lights
+ if (this.renderType == "deferred") {
+ var lights = viewarea.getLights();
+ for (var i = 0; i < lights.length; i++) {
+ if (lights[i]._dirty) {
+
+ if (x3dom.isa(lights[i], x3dom.nodeTypes.DirectionalLight)) {
+ this.object.setDirectionalLight({ id: lights[i]._lightID,
+ on: lights[i]._vf.on,
+ color: lights[i]._vf.color.toGL(),
+ intensity: lights[i]._vf.intensity,
+ ambientIntensity: lights[i]._vf.ambientIntensity,
+ direction: lights[i]._vf.direction.toGL() });
+ }
+ else if (x3dom.isa(lights[i], x3dom.nodeTypes.PointLight)) {
+ var light_transform = mat_view.mult(lights[i].getCurrentTransform());
+
+ this.object.setPointLight({ id: lights[i]._lightID,
+ on: lights[i]._vf.on,
+ color: lights[i]._vf.color.toGL(),
+ intensity: lights[i]._vf.intensity,
+ ambientIntensity: lights[i]._vf.ambientIntensity,
+ attenuation: lights[i]._vf.attenuation.toGL(),
+ location: lights[i]._vf.location.toGL(),
+ radius: lights[i]._vf.radius });
+ }
+ else if (x3dom.isa(lights[i], x3dom.nodeTypes.SpotLight)) {
+ /*this.object.setSpotLight( { id: lights[i]._lightID,
+ on: lights[i]._vf.on,
+ color: lights[i]._vf.color.toGL(),
+ intensity: lights[i]._vf.color.toGL(),
+ ambientIntensity: lights[i]._vf.ambientIntensity,
+ direction: lights[i]._vf.direction.toGL(),
+ attenuation: lights[i]._vf.attenuation.toGL(),
+ location: lights[i]._vf.location.toGL(),
+ radius: lights[i]._vf.radius,
+ beamWidth: lights[i]._vf.beamWidth,
+ cutOffAngle: lights[i]._vf.cutOffAngle } );*/
+ }
+ lights[i]._dirty = false;
+ }
+ }
+ }
+ };
+
+ /** setup Background
+ *
+ */
+ Context.prototype.setupBackground = function (background) {
+ //If background dirty -> update
+ if (background._dirty) {
+ this.object.setBackground({ texURLs: background.getTexUrl(),
+ skyAngle: background._vf.skyAngle,
+ skyColor: background.getSkyColor().toGL(),
+ groundAngle: background._vf.groundAngle,
+ groundColor: background.getGroundColor().toGL(),
+ transparency: background.getTransparency() });
+ background._dirty = false;
+ }
+ };
+
+ /** setup Fog
+ *
+ */
+ Context.prototype.setupFog = function (fog) {
+ if (!fog || !fog._vf || fog._vf.visibilityRange <= 0.0) {
+ this.object.setFog({
+ color: null,
+ visibilityRange: -1.0,
+ fogType: -1.0
+ });
+ return;
+ };
+
+ this.object.setFog({
+ color: fog._vf.color.toGL(),
+ visibilityRange: fog._vf.visibilityRange,
+ fogType: (fog._vf.fogType === "LINEAR") ? 0.0 : 1.0
+ });
+ };
+
+ /** setup Shape
+ *
+ */
+ Context.prototype.setupShape = function (shape, trafo, refID) {
+
+ //Check shape geometry type
+ if (x3dom.isa(shape._cf.geometry.node, x3dom.nodeTypes.PointSet)) {
+ x3dom.debug.logError("Flash backend doesn't support PointSets yet");
+ } else if (x3dom.isa(shape._cf.geometry.node, x3dom.nodeTypes.IndexedLineSet)) {
+ x3dom.debug.logError("Flash backend doesn't support LineSets yet");
+ } else if (x3dom.isa(shape._cf.geometry.node, x3dom.nodeTypes.Text)) {
+ this.setupText(shape, trafo, refID);
+ } else {
+ this.setupIndexedFaceSet(shape, trafo, refID);
+ }
+ };
+
+ Context.prototype.setupIndexedFaceSet = function (shape, trafo, refID) {
+ //Set modelMatrix
+ this.object.setMeshTransform({ id: shape._objectID,
+ refID: refID,
+ transform: trafo.toGL() });
+ if (refID == 0) {
+ //Check if is ImageGeometry or BinaryGeometry
+ var isImageGeometry = x3dom.isa(shape._cf.geometry.node, x3dom.nodeTypes.ImageGeometry);
+ var isBinaryGeometry = x3dom.isa(shape._cf.geometry.node, x3dom.nodeTypes.BinaryGeometry);
+
+ //Check if Appearance is available
+ var appearance = shape._cf.appearance.node;
+ var sortType = (appearance) ? shape._cf.appearance.node._vf.sortType : "auto";
+ var sortKey = (appearance) ? shape._cf.appearance.node._vf.sortKey : 0
+
+ //Set Mesh Properties
+ if (isImageGeometry) {
+ this.object.setMeshProperties({ id: shape._objectID,
+ type: "ImageGeometry",
+ sortType: sortType,
+ sortKey: sortKey,
+ solid: shape.isSolid(),
+ bboxMin: shape._cf.geometry.node.getMin().toGL(),
+ bboxMax: shape._cf.geometry.node.getMax().toGL(),
+ bboxCenter: shape._cf.geometry.node.getCenter().toGL(),
+ primType: shape._cf.geometry.node._vf.primType,
+ vertexCount: shape._cf.geometry.node._vf.vertexCount });
+ } else if (isBinaryGeometry) {
+ this.object.setMeshProperties({ id: shape._objectID,
+ type: "BinaryGeometry",
+ sortType: sortType,
+ sortKey: sortKey,
+ solid: shape.isSolid(),
+ bgCenter: shape._cf.geometry.node._vf.position.toGL(),
+ bgSize: shape._cf.geometry.node._vf.size.toGL(),
+ bboxCenter: shape._cf.geometry.node.getCenter().toGL(),
+ primType: shape._cf.geometry.node._vf.primType,
+ vertexCount: shape._cf.geometry.node._vf.vertexCount });
+ } else {
+ this.object.setMeshProperties({ id: shape._objectID,
+ type: "Default",
+ sortType: sortType,
+ sortKey: sortKey,
+ solid: shape.isSolid() });
+ }
+
+ //Set indices
+ if (shape._dirty.indexes === true) {
+ if (isImageGeometry) {
+ //TODO new flash IG implementation
+ /*this.object.setMeshIndices( { id: shape._objectID,
+ idx: 0,
+ indices: shape._cf.geometry.node.getIndexTextureURL() } );*/
+ } else if (isBinaryGeometry) {
+ this.object.setMeshIndices({ id: shape._objectID,
+ idx: 0,
+ indices: shape._nameSpace.getURL(shape._cf.geometry.node._vf.index) });
+ } else {
+ //If Mesh is multi indexed we have to split it in Flash
+ if (shape._cf.geometry.node._mesh._multiIndIndices && shape._cf.geometry.node._mesh._multiIndIndices.length)
+ {
+ shape._cf.geometry.node._mesh.splitMesh(3, true);
+ }
+
+ for (var i = 0; i < shape._cf.geometry.node._mesh._indices.length; i++) {
+ this.object.setMeshIndices({ id: shape._objectID,
+ idx: i,
+ indices: shape._cf.geometry.node._mesh._indices[i] });
+ }
+ }
+ shape._dirty.indexes = false;
+ }
+
+ //Set vertices
+ if (shape._dirty.positions === true) {
+ if (isImageGeometry) {
+ this.object.setMeshVertices({ id: shape._objectID,
+ idx: 0,
+ //TODO new flash IG implementation coords: shape._cf.geometry.node.getCoordinateTextureURLs(),
+ coordinateTexture0: shape._cf.geometry.node.getCoordinateTextureURL(0),
+ coordinateTexture1: shape._cf.geometry.node.getCoordinateTextureURL(1) });
+ } else if (isBinaryGeometry) {
+ this.object.setMeshVertices({ id: shape._objectID,
+ idx: 0,
+ interleaved: shape._cf.geometry.node._hasStrideOffset,
+ vertices: shape._nameSpace.getURL(shape._cf.geometry.node._vf.coord),
+ normals: shape._nameSpace.getURL(shape._cf.geometry.node._vf.normal),
+ texCoords: shape._nameSpace.getURL(shape._cf.geometry.node._vf.texCoord),
+ colors: shape._nameSpace.getURL(shape._cf.geometry.node._vf.color),
+ numColorComponents: shape._cf.geometry.node._mesh._numColComponents,
+ numNormalComponents: shape._cf.geometry.node._mesh._numNormComponents,
+ vertexType: shape._cf.geometry.node._vf.coordType,
+ normalType: shape._cf.geometry.node._vf.normalType,
+ texCoordType: shape._cf.geometry.node._vf.texCoordType,
+ colorType: shape._cf.geometry.node._vf.colorType,
+ vertexStrideOffset: shape._coordStrideOffset,
+ normalStrideOffset: shape._normalStrideOffset,
+ texCoordStrideOffset: shape._texCoordStrideOffset,
+ colorStrideOffset: shape._colorStrideOffset });
+ } else {
+ for (var i = 0; i < shape._cf.geometry.node._mesh._positions.length; i++) {
+ this.object.setMeshVertices({ id: shape._objectID,
+ idx: i,
+ vertices: shape._cf.geometry.node._mesh._positions[i] });
+ }
+ }
+ shape._dirty.positions = false;
+ }
+
+ //Set normals
+ if (shape._dirty.normals === true) {
+ if (isImageGeometry) {
+ this.object.setMeshNormals({ id: shape._objectID,
+ idx: 0,
+ //TODO new flash IG implementation normals: shape._cf.geometry.node.getNormalTextureURLs(),
+ normalTexture: shape._cf.geometry.node.getNormalTextureURL() });
+ } else if (isBinaryGeometry) {
+ if (!shape._cf.geometry.node._hasStrideOffset) {
+ this.object.setMeshNormals({ id: shape._objectID,
+ idx: 0,
+ normals: shape._nameSpace.getURL(shape._cf.geometry.node._vf.normal) });
+ }
+ } else {
+ if (shape._cf.geometry.node._mesh._normals[0].length) {
+ for (var i = 0; i < shape._cf.geometry.node._mesh._normals.length; i++) {
+ this.object.setMeshNormals({ id: shape._objectID,
+ idx: i,
+ normals: shape._cf.geometry.node._mesh._normals[i] });
+ }
+ }
+ }
+ shape._dirty.normals = false;
+ }
+
+ //Set colors
+ if (shape._dirty.colors === true) {
+ if (isImageGeometry) {
+ this.object.setMeshColors({ id: shape._objectID,
+ idx: 0,
+ colorTexture: shape._cf.geometry.node.getColorTextureURL(),
+ components: shape._cf.geometry.node._mesh._numColComponents });
+ } else if (isBinaryGeometry) {
+ if (!shape._cf.geometry.node._hasStrideOffset) {
+ this.object.setMeshColors({ id: shape._objectID,
+ idx: 0,
+ colors: shape._nameSpace.getURL(shape._cf.geometry.node._vf.color),
+ components: shape._cf.geometry.node._mesh._numColComponents });
+ }
+ } else {
+ if (shape._cf.geometry.node._mesh._colors[0].length) {
+ for (var i = 0; i < shape._cf.geometry.node._mesh._colors.length; i++) {
+ this.object.setMeshColors({ id: shape._objectID,
+ idx: i,
+ colors: shape._cf.geometry.node._mesh._colors[i],
+ components: shape._cf.geometry.node._mesh._numColComponents });
+ }
+ }
+ }
+ shape._dirty.colors = false;
+ }
+
+ //Set texture coordinates
+ if (shape._dirty.texcoords === true) {
+ if (isImageGeometry) {
+ this.object.setMeshTexCoords({ id: shape._objectID,
+ idx: 0,
+ texCoordTexture: shape._cf.geometry.node.getTexCoordTextureURL() });
+ } else if (isBinaryGeometry) {
+ if (!shape._cf.geometry.node._hasStrideOffset) {
+ this.object.setMeshTexCoords({ id: shape._objectID,
+ idx: 0,
+ texCoords: shape._nameSpace.getURL(shape._cf.geometry.node._vf.texCoord) });
+ }
+ } else {
+ if (shape._cf.geometry.node._mesh._texCoords[0].length) {
+ for (var i = 0; i < shape._cf.geometry.node._mesh._texCoords.length; i++) {
+ this.object.setMeshTexCoords({ id: shape._objectID,
+ idx: i,
+ texCoords: shape._cf.geometry.node._mesh._texCoords[i] });
+ }
+ }
+ }
+ shape._dirty.texcoords = false;
+ }
+
+ //Set material
+ if (shape._dirty.material === true) {
+ if (appearance) {
+ var material = shape._cf.appearance.node._cf.material.node;
+ if (material) {
+ this.object.setMeshMaterial({ id: shape._objectID,
+ ambientIntensity: material._vf.ambientIntensity,
+ diffuseColor: material._vf.diffuseColor.toGL(),
+ emissiveColor: material._vf.emissiveColor.toGL(),
+ shininess: material._vf.shininess,
+ specularColor: material._vf.specularColor.toGL(),
+ transparency: material._vf.transparency });
+ }
+ }
+ shape._dirty.material = false;
+ }
+
+ //Set Texture
+ if (shape._dirty.texture === true) {
+ if (appearance) {
+ var texTrafo = null;
+ if (appearance._cf.textureTransform.node) {
+ texTrafo = appearance.texTransformMatrix().toGL();
+ }
+
+ var texture = shape._cf.appearance.node._cf.texture.node;
+
+ if (texture) {
+ if (x3dom.isa(texture, x3dom.nodeTypes.PixelTexture)) {
+ this.object.setPixelTexture({ id: shape._objectID,
+ width: texture._vf.image.width,
+ height: texture._vf.image.height,
+ comp: texture._vf.image.comp,
+ pixels: texture._vf.image.toGL() });
+ } else if (x3dom.isa(texture, x3dom.nodeTypes.ComposedCubeMapTexture)) {
+ this.object.setCubeTexture({ id: shape._objectID,
+ texURLs: texture.getTexUrl() });
+ } else if (texture._isCanvas && texture._canvas) {
+ this.object.setCanvasTexture({ id: shape._objectID,
+ width: texture._canvas.width,
+ height: texture._canvas.height,
+ dataURL: texture._canvas.toDataURL() });
+ } else if (x3dom.isa(texture, x3dom.nodeTypes.MultiTexture)) {
+ x3dom.debug.logError("Flash backend doesn't support MultiTextures yet");
+ } else if (x3dom.isa(texture, x3dom.nodeTypes.MovieTexture)) {
+ x3dom.debug.logError("Flash backend doesn't support MovieTextures yet");
+ } else {
+ this.object.setMeshTexture({ id: shape._objectID,
+ origChannelCount: texture._vf.origChannelCount,
+ repeatS: texture._vf.repeatS,
+ repeatT: texture._vf.repeatT,
+ url: texture._vf.url[0],
+ transform: texTrafo });
+ }
+ } else {
+ this.object.removeTexture({ id: shape._objectID });
+ }
+ }
+ shape._dirty.texture = false;
+ }
+
+ //Set sphere mapping
+ if (shape._cf.geometry.node._cf.texCoord !== undefined &&
+ shape._cf.geometry.node._cf.texCoord.node !== null &&
+ !x3dom.isa(shape._cf.geometry.node._cf.texCoord.node, x3dom.nodeTypes.X3DTextureNode) &&
+ shape._cf.geometry.node._cf.texCoord.node._vf.mode) {
+ var texMode = shape._cf.geometry.node._cf.texCoord.node._vf.mode;
+ if (texMode.toLowerCase() == "sphere") {
+ this.object.setSphereMapping({ id: shape._objectID,
+ sphereMapping: 1 });
+ }
+ else {
+ this.object.setSphereMapping({ id: shape._objectID,
+ sphereMapping: 0 });
+ }
+ }
+ else {
+ this.object.setSphereMapping({ id: shape._objectID,
+ sphereMapping: 0 });
+ }
+ }
+ };
+
+ Context.prototype.setupText = function (shape, trafo, refID) {
+ //Set modelMatrix
+ this.object.setMeshTransform({ id: shape._objectID,
+ refID: refID,
+ transform: trafo.toGL() });
+
+ if (refID == 0) {
+
+ /*this.object.setMeshProperties( { id: shape._objectID,
+ type: "Text",
+ solid: shape.isSolid() } );*/
+
+ //Check if Appearance is available
+ var appearance = shape._cf.appearance.node;
+ var sortType = (appearance) ? shape._cf.appearance.node._vf.sortType : "auto";
+ var sortKey = (appearance) ? shape._cf.appearance.node._vf.sortKey : 0
+
+ if (shape._dirty.text === true) {
+ var fontStyleNode = shape._cf.geometry.node._cf.fontStyle.node;
+ if (fontStyleNode === null) {
+ this.object.setMeshProperties({ id: shape._objectID,
+ type: "Text",
+ sortType: sortType,
+ sortKey: sortKey,
+ solid: shape.isSolid(),
+ text: shape._cf.geometry.node._vf.string,
+ fontFamily: ['SERIF'],
+ fontStyle: "PLAIN",
+ fontAlign: "BEGIN",
+ fontSize: 32,
+ fontSpacing: 1.0,
+ fontHorizontal: true,
+ fontLanguage: "",
+ fontLeftToRight: true,
+ fontTopToBottom: true });
+ } else {
+ this.object.setMeshProperties({ id: shape._objectID,
+ type: "Text",
+ sortType: sortType,
+ sortKey: sortKey,
+ solid: shape.isSolid(),
+ text: shape._cf.geometry.node._vf.string,
+ fontFamily: fontStyleNode._vf.family.toString(),
+ fontStyle: fontStyleNode._vf.style.toString(),
+ fontAlign: fontStyleNode._vf.justify.toString(),
+ fontSize: fontStyleNode._vf.size,
+ fontSpacing: fontStyleNode._vf.spacing,
+ fontHorizontal: fontStyleNode._vf.horizontal,
+ fontLanguage: fontStyleNode._vf.language,
+ fontLeftToRight: fontStyleNode._vf.leftToRight,
+ fontTopToBottom: fontStyleNode._vf.topToBottom });
+ }
+ shape._dirty.text = false;
+ }
+
+ if (shape._dirty.material === true) {
+ if (appearance) {
+ var material = shape._cf.appearance.node._cf.material.node;
+ if (material) {
+ this.object.setMeshMaterial({ id: shape._objectID,
+ ambientIntensity: material._vf.ambientIntensity,
+ diffuseColor: material._vf.diffuseColor.toGL(),
+ emissiveColor: material._vf.emissiveColor.toGL(),
+ shininess: material._vf.shininess,
+ specularColor: material._vf.specularColor.toGL(),
+ transparency: material._vf.transparency });
+ }
+ }
+ shape._dirty.material = false;
+ }
+ }
+ };
+
+
+ /** pick Value
+ *
+ */
+ Context.prototype.pickValue = function (viewarea, x, y, viewMat, sceneMat) {
+ var scene = viewarea._scene;
+
+ // method requires that scene has already been rendered at least once
+ if (this.object === null || scene === null || scene.drawableCollection === undefined || !scene.drawableCollection || scene._vf.pickMode.toLowerCase() === "box") {
+ return false;
+ }
+
+ var pickMode = (scene._vf.pickMode.toLowerCase() === "color") ? 1 :
+ ((scene._vf.pickMode.toLowerCase() === "texcoord") ? 2 : 0);
+
+ var data = this.object.pickValue({ pickMode: pickMode });
+
+ if (data.objID > 0) {
+ viewarea._pickingInfo.pickPos = new x3dom.fields.SFVec3f(data.pickPosX, data.pickPosY, data.pickPosZ);
+ viewarea._pickingInfo.pickObj = x3dom.nodeTypes.Shape.idMap.nodeID[data.objID];
+ } else {
+ viewarea._pickingInfo.pickObj = null;
+ viewarea._pickingInfo.lastClickObj = null;
+ }
+
+ return true;
+ };
+
+ /** shutdown
+ *
+ */
+ Context.prototype.shutdown = function (viewarea) {
+ // TODO?
+ };
+
+ //Return the setup context function
+ return setupContext;
+})();
+
+/*
+ * X3DOM JavaScript Library
+ * http://www.x3dom.org
+ *
+ * (C)2009 Fraunhofer IGD, Darmstadt, Germany
+ * Dual licensed under the MIT and GPL
+ *
+ * Based on code originally provided by
+ * Philip Taylor: http://philip.html5.org
+ */
+
+/// NodeNameSpace constructor
+x3dom.NodeNameSpace = function (name, document) {
+ this.name = name;
+ this.doc = document;
+ this.baseURL = "";
+ this.defMap = {};
+ this.parent = null;
+ this.childSpaces = [];
+};
+
+x3dom.NodeNameSpace.prototype.addNode = function (node, name) {
+ this.defMap[name] = node;
+ node._nameSpace = this;
+};
+
+x3dom.NodeNameSpace.prototype.removeNode = function (name) {
+ var node = name ? this.defMap[name] : null;
+ if (node) {
+ delete this.defMap[name];
+ node._nameSpace = null;
+ }
+};
+
+x3dom.NodeNameSpace.prototype.getNamedNode = function (name) {
+ return this.defMap[name];
+};
+
+x3dom.NodeNameSpace.prototype.getNamedElement = function (name) {
+ var node = this.defMap[name];
+ return (node ? node._xmlNode : null);
+};
+
+x3dom.NodeNameSpace.prototype.addSpace = function (space) {
+ this.childSpaces.push(space);
+ space.parent = this;
+};
+
+x3dom.NodeNameSpace.prototype.removeSpace = function (space) {
+ space.parent = null;
+ for (var it=0; it<this.childSpaces.length; it++) {
+ if (this.childSpaces[it] == space) {
+ this.childSpaces.splice(it, 1);
+ }
+ }
+};
+
+x3dom.NodeNameSpace.prototype.setBaseURL = function (url) {
+ var i = url.lastIndexOf ("/");
+ this.baseURL = (i >= 0) ? url.substr(0,i+1) : "";
+
+ x3dom.debug.logInfo("setBaseURL: " + this.baseURL);
+};
+
+x3dom.NodeNameSpace.prototype.getURL = function (url) {
+ if (url === undefined || !url.length) {
+ return "";
+ }
+ else {
+ return ((url[0] === '/') || (url.indexOf(":") >= 0)) ? url : (this.baseURL + url);
+ }
+};
+
+// helper to check an element's attribute
+x3dom.hasElementAttribute = function(attrName)
+{
+ var ok = this.__hasAttribute(attrName);
+ if (!ok && attrName) {
+ ok = this.__hasAttribute(attrName.toLowerCase());
+ }
+ return ok;
+};
+
+// helper to get an element's attribute
+x3dom.getElementAttribute = function(attrName)
+{
+ var attrib = this.__getAttribute(attrName);
+ if (!attrib && attrib != "" && attrName) {
+ attrib = this.__getAttribute(attrName.toLowerCase());
+ }
+
+ if (attrib || !this._x3domNode) {
+ return attrib;
+ }
+ else {
+ return this._x3domNode._vf[attrName];
+ }
+};
+
+// helper to set an element's attribute
+x3dom.setElementAttribute = function(attrName, newVal)
+{
+ //var prevVal = this.getAttribute(attrName);
+ this.__setAttribute(attrName, newVal);
+ //newVal = this.getAttribute(attrName);
+
+ var x3dNode = this._x3domNode;
+ if (x3dNode) {
+ x3dNode.updateField(attrName, newVal);
+ x3dNode._nameSpace.doc.needRender = true;
+ }
+};
+
+/**
+ * Returns the value of the field with the given name.
+ * The value is returned as an object of the corresponding field type.
+ *
+ * @param {String} fieldName - the name of the field
+ */
+x3dom.getFieldValue = function(fieldName)
+{
+ var x3dNode = this._x3domNode;
+
+ if (x3dNode && x3dNode._vf[fieldName]) {
+ return x3dNode._vf[fieldName].copy();
+ }
+
+ return null;
+};
+
+
+/**
+ * Sets the value of the field with the given name to the given value.
+ * The value is specified as an object of the corresponding field type.
+ *
+ * @param {String} fieldName - the name of the field where the value should be set
+ * @param {String} fieldvalue - the new field value
+ */
+x3dom.setFieldValue = function(fieldName, fieldvalue) {
+ var x3dNode = this._x3domNode;
+ if (x3dNode && x3dNode._vf[fieldName]) {
+
+ // SF/MF object types are cloned based on a copy function
+ if(fieldvalue instanceof Object && 'copy' in fieldvalue)
+ {
+ x3dNode._vf[fieldName] = fieldvalue.copy();
+ }
+ //f.i. SFString SFBool aren't objects
+ else
+ x3dNode._vf[fieldName] = fieldvalue;
+
+ x3dNode.fieldChanged(fieldName);
+ x3dNode._nameSpace.doc.needRender = true;
+ }
+};
+
+
+/**
+ * Returns the field object of the field with the given name.
+ * The returned object is no copy, but instead a reference to X3DOM's internal field object.
+ * Changes to this object should be committed using the returnFieldRef function.
+ * Note: this only works for fields with pointer types such as MultiFields!
+ *
+ * @param {String} fieldName - the name of the field
+ */
+x3dom.requestFieldRef = function(fieldName)
+{
+ var x3dNode = this._x3domNode;
+ if (x3dNode && x3dNode._vf[fieldName])
+ {
+ return x3dNode._vf[fieldName];
+ }
+
+ return null;
+};
+
+
+/**
+ * Commits all changes made to the internal field object of the field with the given name.
+ * This must be done in order to notify X3DOM to process all related changes internally.
+ *
+ * @param {String} fieldName - the name of the field
+ */
+x3dom.releaseFieldRef = function(fieldName)
+{
+ var x3dNode = this._x3domNode;
+ if (x3dNode && x3dNode._vf[fieldName])
+ {
+ x3dNode.fieldChanged(fieldName);
+ x3dNode._nameSpace.doc.needRender = true;
+ }
+};
+
+
+x3dom.NodeNameSpace.prototype.setupTree = function (domNode) {
+ var n = null;
+
+ if (x3dom.isX3DElement(domNode)) {
+
+ // return if it is already initialized
+ if (domNode._x3domNode) {
+ x3dom.debug.logWarning('Tree is already initialized');
+ return null;
+ }
+
+ // workaround since one cannot find out which handlers are registered
+ if ( (domNode.tagName !== undefined) &&
+ (!domNode.__addEventListener) && (!domNode.__removeEventListener) )
+ {
+ // helper to track an element's listeners
+ domNode.__addEventListener = domNode.addEventListener;
+ domNode.addEventListener = function(type, func, phase) {
+ if (!this._x3domNode._listeners[type]) {
+ this._x3domNode._listeners[type] = [];
+ }
+ this._x3domNode._listeners[type].push(func);
+
+ //x3dom.debug.logInfo('addEventListener for ' + this.tagName + ".on" + type);
+ this.__addEventListener(type, func, phase);
+ };
+
+ domNode.__removeEventListener = domNode.removeEventListener;
+ domNode.removeEventListener = function(type, func, phase) {
+ var list = this._x3domNode._listeners[type];
+ if (list) {
+ for (var it=0; it<list.length; it++) {
+ if (list[it] == func) {
+ list.splice(it, 1);
+ //x3dom.debug.logInfo('removeEventListener for ' +
+ // this.tagName + ".on" + type);
+ }
+ }
+ }
+ this.__removeEventListener(type, func, phase);
+ };
+ }
+
+ // TODO (?): dynamic update of USE attribute during runtime
+ if (domNode.hasAttribute('USE') || domNode.hasAttribute('use'))
+ {
+ //fix usage of lowercase 'use'
+ if (!domNode.hasAttribute('USE')) {
+ domNode.setAttribute('USE', domNode.getAttribute('use'));
+ }
+
+ n = this.defMap[domNode.getAttribute('USE')];
+ if (!n) {
+ var nsName = domNode.getAttribute('USE').split('__');
+
+ if (nsName.length >= 2) {
+ var otherNS = this;
+ while (otherNS) {
+ if (otherNS.name == nsName[0])
+ n = otherNS.defMap[nsName[1]];
+ if (n)
+ otherNS = null;
+ else
+ otherNS = otherNS.parent;
+ }
+ if (!n) {
+ n = null;
+ x3dom.debug.logWarning('Could not USE: ' + domNode.getAttribute('USE'));
+ }
+ }
+ }
+ if (n) {
+ domNode._x3domNode = n;
+ }
+ return n;
+ }
+ else {
+ // check and create ROUTEs
+ if (domNode.localName.toLowerCase() === 'route') {
+ var route = domNode;
+ var fnAtt = route.getAttribute('fromNode') || route.getAttribute('fromnode');
+ var tnAtt = route.getAttribute('toNode') || route.getAttribute('tonode');
+ var fromNode = this.defMap[fnAtt];
+ var toNode = this.defMap[tnAtt];
+ if (! (fromNode && toNode)) {
+ x3dom.debug.logWarning("Broken route - can't find all DEFs for " + fnAtt + " -> " + tnAtt);
+ }
+ else {
+ //x3dom.debug.logInfo("ROUTE: from=" + fromNode._DEF + ", to=" + toNode._DEF);
+ fnAtt = route.getAttribute('fromField') || route.getAttribute('fromfield');
+ tnAtt = route.getAttribute('toField') || route.getAttribute('tofield');
+ fromNode.setupRoute(fnAtt, toNode, tnAtt);
+ // Store reference to namespace for being able to remove route later on
+ route._nodeNameSpace = this;
+ }
+ return null;
+ }
+
+ //attach X3DOM's custom field interface functions
+ domNode.requestFieldRef = x3dom.requestFieldRef;
+ domNode.releaseFieldRef = x3dom.releaseFieldRef;
+ domNode.getFieldValue = x3dom.getFieldValue;
+ domNode.setFieldValue = x3dom.setFieldValue;
+
+ // find the NodeType for the given dom-node
+ var nodeType = x3dom.nodeTypesLC[domNode.localName.toLowerCase()];
+ if (nodeType === undefined) {
+ x3dom.debug.logWarning("Unrecognised X3D element &lt;" + domNode.localName + "&gt;.");
+ }
+ else {
+ //active workaround for missing DOMAttrModified support
+ if ( (x3dom.userAgentFeature.supportsDOMAttrModified === false)
+ && (domNode instanceof Element) ) {
+ if (domNode.setAttribute && !domNode.__setAttribute) {
+ domNode.__setAttribute = domNode.setAttribute;
+ domNode.setAttribute = x3dom.setElementAttribute;
+ }
+
+ if (domNode.getAttribute && !domNode.__getAttribute) {
+ domNode.__getAttribute = domNode.getAttribute;
+ domNode.getAttribute = x3dom.getElementAttribute;
+ }
+
+ if (domNode.hasAttribute && !domNode.__hasAttribute) {
+ domNode.__hasAttribute = domNode.hasAttribute;
+ domNode.hasAttribute = x3dom.hasElementAttribute;
+ }
+ }
+
+ // create x3domNode
+ var ctx = {
+ doc: this.doc,
+ xmlNode: domNode,
+ nameSpace: this
+ };
+ n = new nodeType(ctx);
+
+ // find and store/link _DEF name
+ if (domNode.hasAttribute('DEF')) {
+ n._DEF = domNode.getAttribute('DEF');
+ this.defMap[n._DEF] = n;
+ }
+ else {
+ if (domNode.hasAttribute('id')) {
+ n._DEF = domNode.getAttribute('id');
+ this.defMap[n._DEF] = n;
+ }
+ }
+
+ // add experimental highlighting functionality
+ if (domNode.highlight === undefined)
+ {
+ domNode.highlight = function(enable, colorStr) {
+ var color = x3dom.fields.SFColor.parse(colorStr);
+ this._x3domNode.highlight(enable, color);
+ this._x3domNode._nameSpace.doc.needRender = true;
+ };
+ }
+
+ // link both DOM-Node and Scene-graph-Node
+ n._xmlNode = domNode;
+ domNode._x3domNode = n;
+
+ // call children
+ var that = this;
+ Array.forEach ( domNode.childNodes, function (childDomNode) {
+ var c = that.setupTree(childDomNode);
+ if (c) {
+ n.addChild(c, childDomNode.getAttribute("containerField"));
+ }
+ } );
+
+ n.nodeChanged();
+ return n;
+ }
+ }
+ }
+ else if (domNode.localName) {
+ // be nice to users who use nodes not (yet) known to the system
+ x3dom.debug.logWarning("Unrecognised X3D element &lt;" + domNode.localName + "&gt;.");
+ n = null;
+ }
+
+ return n;
+};
+
+/** @namespace x3dom.nodeTypes */
+/*
+ * X3DOM JavaScript Library
+ * http://www.x3dom.org
+ *
+ * (C)2009 Fraunhofer IGD, Darmstadt, Germany
+ * Dual licensed under the MIT and GPL
+ */
+
+// ### X3DNode ###
+x3dom.registerNodeType(
+ "X3DNode",
+ "Core",
+ defineClass(null,
+ /**
+ * Constructor for X3DNode
+ * @constructs x3dom.nodeTypes.X3DNode
+ * @x3d 3.3
+ * @component Core
+ * @status experimental
+ * @param {Object} [ctx=null] - context object, containing initial settings like namespace
+ * @classdesc This abstract node type is the base type for all nodes in the X3D system.
+ */
+ function (ctx) {
+ // reference to DOM element
+ this._xmlNode = null;
+
+ // holds a link to the node name
+ this._DEF = null;
+
+ // links the nameSpace
+ this._nameSpace = (ctx && ctx.nameSpace) ? ctx.nameSpace : null;
+
+ // holds all value fields (e.g. SFFloat, MFVec3f, ...)
+ this._vf = {};
+ this._vfFieldTypes = {};
+
+ // holds all child fields ( SFNode and MFNode )
+ this._cf = {};
+ this._cfFieldTypes = {};
+
+ this._fieldWatchers = {};
+ this._routes = {};
+
+ this._listeners = {};
+
+ this._parentNodes = [];
+
+ // FIXME; should be removed and handled by _cf methods
+ this._childNodes = [];
+
+
+ /**
+ * Field to add metadata information
+ * @var {x3dom.fields.SFNode} metadata
+ * @memberof x3dom.nodeTypes.X3DNode
+ * @initvalue X3DMetadataObject
+ * @field x3d
+ * @instance
+ */
+ this.addField_SFNode('metadata', x3dom.nodeTypes.X3DMetadataObject);
+
+ },
+ {
+ type: function () {
+ return this.constructor;
+ },
+
+ typeName: function () {
+ return this.constructor._typeName;
+ },
+
+ addChild: function (node, containerFieldName) {
+ if (node) {
+ var field = null;
+ if (containerFieldName) {
+ field = this._cf[containerFieldName];
+ }
+ else {
+ for (var fieldName in this._cf) {
+ if (this._cf.hasOwnProperty(fieldName)) {
+ var testField = this._cf[fieldName];
+ if (x3dom.isa(node,testField.type)) {
+ field = testField;
+ break;
+ }
+ }
+ }
+ }
+ if (field && field.addLink(node)) {
+ node._parentNodes.push(this);
+ this._childNodes.push(node);
+ node.parentAdded(this);
+ return true;
+ }
+ }
+ return false;
+ },
+
+ removeChild: function (node) {
+ if (node) {
+ for (var fieldName in this._cf) {
+ if (this._cf.hasOwnProperty(fieldName)) {
+ var field = this._cf[fieldName];
+ if (field.rmLink(node)) {
+ for (var i = node._parentNodes.length - 1; i >= 0; i--) {
+ if (node._parentNodes[i] === this) {
+ node._parentNodes.splice(i, 1);
+ node.parentRemoved(this);
+ }
+ }
+ for (var j = this._childNodes.length - 1; j >= 0; j--) {
+ if (this._childNodes[j] === node) {
+ node.onRemove();
+ this._childNodes.splice(j, 1);
+ return true;
+ }
+ }
+ }
+ }
+ }
+ }
+ return false;
+ },
+
+ onRemove: function() {
+ // to be overwritten by concrete classes
+ },
+
+ parentAdded: function(parent) {
+ // to be overwritten by concrete classes
+ },
+
+ parentRemoved: function(parent) {
+ // attention: overwritten by concrete classes
+ for (var i=0, n=this._childNodes.length; i<n; i++) {
+ if (this._childNodes[i]) {
+ this._childNodes[i].parentRemoved(this);
+ }
+ }
+ },
+
+ getCurrentTransform: function () {
+ if (this._parentNodes.length >= 1) {
+ return this.transformMatrix(this._parentNodes[0].getCurrentTransform());
+ }
+ else {
+ return x3dom.fields.SFMatrix4f.identity();
+ }
+ },
+
+ transformMatrix: function (transform) {
+ return transform;
+ },
+
+ getVolume: function () {
+ //x3dom.debug.logWarning("Called getVolume for unbounded node!");
+ return null;
+ },
+
+ invalidateVolume: function() {
+ // overwritten
+ },
+
+ invalidateCache: function() {
+ // overwritten
+ },
+
+ volumeValid: function() {
+ return false;
+ },
+
+ // Collects all objects to be drawn
+ collectDrawableObjects: function (transform, drawableCollection, singlePath, invalidateCache, planeMask, clipPlanes) {
+ // explicitly do nothing on collect traversal for (most) nodes
+ },
+
+ highlight: function(enable, color)
+ {
+ if (this._vf.hasOwnProperty("diffuseColor"))
+ {
+ if (enable) {
+ if (this._actDiffuseColor === undefined) {
+ this._actDiffuseColor = new x3dom.fields.SFColor();
+ this._highlightOn = false;
+ }
+
+ if (!this._highlightOn) {
+ this._actDiffuseColor.setValues(this._vf.diffuseColor);
+ this._highlightOn = true;
+ }
+ this._vf.diffuseColor.setValues(color);
+ }
+ else {
+ if (this._actDiffuseColor !== undefined) {
+ this._vf.diffuseColor.setValues(this._actDiffuseColor);
+ this._highlightOn = false;
+ // new/delete every frame can be very slow
+ // but prevent from copying if called not only on change
+ delete this._actDiffuseColor;
+ }
+ }
+ }
+
+ for (var i=0, n=this._childNodes.length; i<n; i++)
+ {
+ if (this._childNodes[i])
+ this._childNodes[i].highlight(enable, color);
+ }
+ },
+
+ findX3DDoc: function () {
+ return this._nameSpace.doc;
+ },
+
+ doIntersect: function(line) {
+ var isect = false;
+ for (var i=0; i<this._childNodes.length; i++) {
+ if (this._childNodes[i]) {
+ isect = this._childNodes[i].doIntersect(line) || isect;
+ }
+ }
+ return isect;
+ },
+
+ postMessage: function (field, msg) {
+ // TODO: timestamps and stuff
+ this._vf[field] = msg; // FIXME; _cf!!!
+ var listeners = this._fieldWatchers[field];
+
+ var that = this;
+ if (listeners) {
+ Array.forEach(listeners, function (l) { l.call(that, msg); });
+ }
+
+ //for Web-style access to the output data of ROUTES, provide a callback function
+ var eventObject = {
+ target: that._xmlNode,
+ type: "outputchange", // event only called onxxx if used as old-fashioned attribute
+ fieldName: field,
+ value: msg
+ };
+
+ this.callEvtHandler("onoutputchange", eventObject);
+ },
+
+ // method for handling field updates
+ updateField: function (field, msg) {
+ var f = this._vf[field];
+
+ if (f === undefined) {
+ for (var key in this._vf) {
+ if (key.toLowerCase() == field) {
+ field = key;
+ f = this._vf[field];
+ break;
+ }
+ }
+
+ var pre = "set_";
+ if (f === undefined && field.indexOf(pre) == 0) {
+ var fieldName = field.substr(pre.length, field.length - 1);
+ if (this._vf[fieldName] !== undefined) {
+ field = fieldName;
+ f = this._vf[field];
+ }
+ }
+ if (f === undefined) {
+ f = null;
+ this._vf[field] = f;
+ }
+ }
+
+ if (f !== null) {
+ try {
+ this._vf[field].setValueByStr(msg);
+ }
+ catch (exc1) {
+ try {
+ switch ((typeof(this._vf[field])).toString()) {
+ case "number":
+ if (typeof(msg) == "number")
+ this._vf[field] = msg;
+ else
+ this._vf[field] = +msg;
+ break;
+ case "boolean":
+ if (typeof(msg) == "boolean")
+ this._vf[field] = msg;
+ else
+ this._vf[field] = (msg.toLowerCase() == "true");
+ break;
+ case "string":
+ this._vf[field] = msg;
+ break;
+ }
+ }
+ catch (exc2) {
+ x3dom.debug.logError("updateField: setValueByStr() NYI for " + typeof(f));
+ }
+ }
+
+ // TODO: eval fieldChanged for all nodes!
+ this.fieldChanged(field);
+ }
+ },
+
+ setupRoute: function (fromField, toNode, toField) {
+ var pos;
+ var fieldName;
+ var pre = "set_", post = "_changed";
+
+ // build correct fromField
+ if (!this._vf[fromField]) {
+ pos = fromField.indexOf(pre);
+ if (pos === 0) {
+ fieldName = fromField.substr(pre.length, fromField.length - 1);
+ if (this._vf[fieldName]) {
+ fromField = fieldName;
+ }
+ } else {
+ pos = fromField.indexOf(post);
+ if (pos > 0) {
+ fieldName = fromField.substr(0, fromField.length - post.length);
+ if (this._vf[fieldName]) {
+ fromField = fieldName;
+ }
+ }
+ }
+ }
+
+ // build correct toField
+ if (!toNode._vf[toField]) {
+ pos = toField.indexOf(pre);
+ if (pos === 0) {
+ fieldName = toField.substr(pre.length, toField.length - 1);
+ if (toNode._vf[fieldName]) {
+ toField = fieldName;
+ }
+ }
+ else {
+ pos = toField.indexOf(post);
+ if (pos > 0) {
+ fieldName = toField.substr(0, toField.length - post.length);
+ if (toNode._vf[fieldName]) {
+ toField = fieldName;
+ }
+ }
+ }
+ }
+
+ var where = this._DEF + "&" + fromField + "&" + toNode._DEF + "&" + toField;
+
+ if (!this._routes[where]) {
+ if (!this._fieldWatchers[fromField]) {
+ this._fieldWatchers[fromField] = [];
+ }
+ this._fieldWatchers[fromField].push(
+ function (msg) {
+ toNode.postMessage(toField, msg);
+ }
+ );
+
+ if (!toNode._fieldWatchers[toField]) {
+ toNode._fieldWatchers[toField] = [];
+ }
+ toNode._fieldWatchers[toField].push(
+ // FIXME: THIS DOESN'T WORK FOR NODE (_cf) FIELDS
+ function (msg) {
+ toNode._vf[toField] = msg;
+ toNode.fieldChanged(toField);
+ }
+ );
+
+ // store this route to be able to delete it
+ this._routes[where] = {
+ from: this._fieldWatchers[fromField].length - 1,
+ to: toNode._fieldWatchers[toField].length - 1
+ };
+ }
+ },
+
+ removeRoute: function (fromField, toNode, toField) {
+ var pos;
+ var fieldName;
+ var pre = "set_", post = "_changed";
+
+ // again, build correct fromField
+ if (!this._vf[fromField]) {
+ pos = fromField.indexOf(pre);
+ if (pos === 0) {
+ fieldName = fromField.substr(pre.length, fromField.length - 1);
+ if (this._vf[fieldName]) {
+ fromField = fieldName;
+ }
+ } else {
+ pos = fromField.indexOf(post);
+ if (pos > 0) {
+ fieldName = fromField.substr(0, fromField.length - post.length);
+ if (this._vf[fieldName]) {
+ fromField = fieldName;
+ }
+ }
+ }
+ }
+
+ // again, build correct toField
+ if (!toNode._vf[toField]) {
+ pos = toField.indexOf(pre);
+ if (pos === 0) {
+ fieldName = toField.substr(pre.length, toField.length - 1);
+ if (toNode._vf[fieldName]) {
+ toField = fieldName;
+ }
+ }
+ else {
+ pos = toField.indexOf(post);
+ if (pos > 0) {
+ fieldName = toField.substr(0, toField.length - post.length);
+ if (toNode._vf[fieldName]) {
+ toField = fieldName;
+ }
+ }
+ }
+ }
+
+ // finally, delete route
+ var where = this._DEF + "&" + fromField + "&" + toNode._DEF + "&" + toField;
+
+ if (this._routes[where]) {
+ this._fieldWatchers[fromField].splice(this._routes[where].from, 1);
+ toNode._fieldWatchers[toField].splice(this._routes[where].to, 1);
+
+ delete this._routes[where];
+ }
+ },
+
+ fieldChanged: function (fieldName) {
+ // to be overwritten by concrete classes
+ },
+
+ nodeChanged: function () {
+ // to be overwritten by concrete classes
+ },
+
+ callEvtHandler: function(eventType, event) {
+ var node = this;
+
+ if (!node._xmlNode) {
+ return event.cancelBubble;
+ }
+
+ try {
+ var attrib = node._xmlNode[eventType];
+ event.target = node._xmlNode;
+
+ if (typeof(attrib) === "function") {
+ attrib.call(node._xmlNode, event);
+ }
+ else {
+ var funcStr = node._xmlNode.getAttribute(eventType);
+ var func = new Function('event', funcStr);
+ func.call(node._xmlNode, event);
+ }
+
+ var list = node._listeners[event.type];
+ if (list) {
+ for (var it=0; it<list.length; it++) {
+ list[it].call(node._xmlNode, event);
+ }
+ }
+ }
+ catch(ex) {
+ x3dom.debug.logException(ex);
+ }
+
+ return event.cancelBubble;
+ },
+
+ initSetter: function (xmlNode, name) {
+ if (!xmlNode || !name)
+ return;
+
+ var nameLC = name.toLowerCase();
+ if (xmlNode.__defineSetter__ && xmlNode.__defineGetter__) {
+ xmlNode.__defineSetter__(name, function(value) {
+ xmlNode.setAttribute(name, value);
+ });
+ xmlNode.__defineGetter__(name, function() {
+ return xmlNode.getAttribute(name);
+ });
+ if (nameLC != name) {
+ xmlNode.__defineSetter__(nameLC, function(value) {
+ xmlNode.setAttribute(name, value);
+ });
+ xmlNode.__defineGetter__(nameLC, function() {
+ return xmlNode.getAttribute(name);
+ });
+ }
+ }
+ else {
+ // IE has no __define[G|S]etter__ !!!
+ Object.defineProperty(xmlNode, name, {
+ set: function(value) {
+ xmlNode.setAttribute(name, value);
+ },
+ get: function() {
+ return xmlNode.getAttribute(name);
+ },
+ configurable: true,
+ enumerable: true
+ });
+ }
+
+ if (this._vf[name] &&
+ !xmlNode.attributes[name] && !xmlNode.attributes[name.toLowerCase()]) {
+ var str = "";
+ try {
+ if (this._vf[name].toGL)
+ str = this._vf[name].toGL().toString();
+ else
+ str = this._vf[name].toString();
+ }
+ catch (e) {
+ str = this._vf[name].toString();
+ }
+ if (!str) {
+ str = "";
+ }
+ xmlNode.setAttribute(name, str);
+ }
+ },
+
+ // single fields
+ addField_SFInt32: function (ctx, name, n) {
+ this._vf[name] = ctx && ctx.xmlNode && ctx.xmlNode.hasAttribute(name) ?
+ parseInt(ctx.xmlNode.getAttribute(name),10) : n;
+
+ if (ctx && ctx.xmlNode) { this.initSetter(ctx.xmlNode, name); }
+ this._vfFieldTypes[name] = "SFInt32";
+ },
+
+ addField_SFFloat: function (ctx, name, n) {
+ this._vf[name] = ctx && ctx.xmlNode && ctx.xmlNode.hasAttribute(name) ?
+ +ctx.xmlNode.getAttribute(name) : n;
+
+ if (ctx && ctx.xmlNode) { this.initSetter(ctx.xmlNode, name); }
+ this._vfFieldTypes[name] = "SFFloat";
+ },
+
+ addField_SFDouble: function (ctx, name, n) {
+ this._vf[name] = ctx && ctx.xmlNode && ctx.xmlNode.hasAttribute(name) ?
+ +ctx.xmlNode.getAttribute(name) : n;
+
+ if (ctx && ctx.xmlNode) { this.initSetter(ctx.xmlNode, name); }
+ this._vfFieldTypes[name] = "SFDouble";
+ },
+
+ addField_SFTime: function (ctx, name, n) {
+ this._vf[name] = ctx && ctx.xmlNode && ctx.xmlNode.hasAttribute(name) ?
+ +ctx.xmlNode.getAttribute(name) : n;
+
+ if (ctx && ctx.xmlNode) { this.initSetter(ctx.xmlNode, name); }
+ this._vfFieldTypes[name] = "SFTime";
+ },
+
+ addField_SFBool: function (ctx, name, n) {
+ this._vf[name] = ctx && ctx.xmlNode && ctx.xmlNode.hasAttribute(name) ?
+ ctx.xmlNode.getAttribute(name).toLowerCase() === "true" : n;
+
+ if (ctx && ctx.xmlNode) { this.initSetter(ctx.xmlNode, name); }
+ this._vfFieldTypes[name] = "SFBool";
+ },
+
+ addField_SFString: function (ctx, name, n) {
+ this._vf[name] = ctx && ctx.xmlNode && ctx.xmlNode.hasAttribute(name) ?
+ ctx.xmlNode.getAttribute(name) : n;
+
+ if (ctx && ctx.xmlNode) { this.initSetter(ctx.xmlNode, name); }
+ this._vfFieldTypes[name] = "SFString";
+ },
+
+ addField_SFColor: function (ctx, name, r, g, b) {
+ this._vf[name] = ctx && ctx.xmlNode && ctx.xmlNode.hasAttribute(name) ?
+ x3dom.fields.SFColor.parse(ctx.xmlNode.getAttribute(name)) :
+ new x3dom.fields.SFColor(r, g, b);
+
+ if (ctx && ctx.xmlNode) { this.initSetter(ctx.xmlNode, name); }
+ this._vfFieldTypes[name] = "SFColor";
+ },
+
+ addField_SFColorRGBA: function (ctx, name, r, g, b, a) {
+ this._vf[name] = ctx && ctx.xmlNode && ctx.xmlNode.hasAttribute(name) ?
+ x3dom.fields.SFColorRGBA.parse(ctx.xmlNode.getAttribute(name)) :
+ new x3dom.fields.SFColorRGBA(r, g, b, a);
+
+ if (ctx && ctx.xmlNode) { this.initSetter(ctx.xmlNode, name); }
+ this._vfFieldTypes[name] = "SFColorRGBA";
+ },
+
+ addField_SFVec2f: function (ctx, name, x, y) {
+ this._vf[name] = ctx && ctx.xmlNode && ctx.xmlNode.hasAttribute(name) ?
+ x3dom.fields.SFVec2f.parse(ctx.xmlNode.getAttribute(name)) :
+ new x3dom.fields.SFVec2f(x, y);
+
+ if (ctx && ctx.xmlNode) { this.initSetter(ctx.xmlNode, name); }
+ this._vfFieldTypes[name] = "SFVec2f";
+ },
+
+ addField_SFVec3f: function (ctx, name, x, y, z) {
+ this._vf[name] = ctx && ctx.xmlNode && ctx.xmlNode.hasAttribute(name) ?
+ x3dom.fields.SFVec3f.parse(ctx.xmlNode.getAttribute(name)) :
+ new x3dom.fields.SFVec3f(x, y, z);
+
+ if (ctx && ctx.xmlNode) { this.initSetter(ctx.xmlNode, name); }
+ this._vfFieldTypes[name] = "SFVec3f";
+ },
+
+ addField_SFVec4f: function (ctx, name, x, y, z, w) {
+ this._vf[name] = ctx && ctx.xmlNode && ctx.xmlNode.hasAttribute(name) ?
+ x3dom.fields.SFVec4f.parse(ctx.xmlNode.getAttribute(name)) :
+ new x3dom.fields.SFVec4f(x, y, z, w);
+
+ if (ctx && ctx.xmlNode) { this.initSetter(ctx.xmlNode, name); }
+ this._vfFieldTypes[name] = "SFVec4f";
+ },
+
+ addField_SFVec3d: function(ctx, name, x, y, z) {
+ this.addField_SFVec3f(ctx, name, x, y, z);
+ this._vfFieldTypes[name] = "SFVec3d";
+ },
+
+ addField_SFRotation: function (ctx, name, x, y, z, a) {
+ this._vf[name] = ctx && ctx.xmlNode && ctx.xmlNode.hasAttribute(name) ?
+ x3dom.fields.Quaternion.parseAxisAngle(ctx.xmlNode.getAttribute(name)) :
+ x3dom.fields.Quaternion.axisAngle(new x3dom.fields.SFVec3f(x, y, z), a);
+
+ if (ctx && ctx.xmlNode) { this.initSetter(ctx.xmlNode, name); }
+ this._vfFieldTypes[name] = "SFRotation";
+ },
+
+ addField_SFMatrix4f: function (ctx, name, _00, _01, _02, _03,
+ _10, _11, _12, _13,
+ _20, _21, _22, _23,
+ _30, _31, _32, _33) {
+ this._vf[name] = ctx && ctx.xmlNode && ctx.xmlNode.hasAttribute(name) ?
+ x3dom.fields.SFMatrix4f.parse(ctx.xmlNode.getAttribute(name)) :
+ new x3dom.fields.SFMatrix4f(_00, _01, _02, _03,
+ _10, _11, _12, _13,
+ _20, _21, _22, _23,
+ _30, _31, _32, _33);
+
+ if (ctx && ctx.xmlNode) { this.initSetter(ctx.xmlNode, name); }
+ this._vfFieldTypes[name] = "SFMatrix4f";
+ },
+
+ addField_SFImage: function (ctx, name, def) {
+ this._vf[name] = ctx && ctx.xmlNode && ctx.xmlNode.hasAttribute(name) ?
+ x3dom.fields.SFImage.parse(ctx.xmlNode.getAttribute(name)) :
+ new x3dom.fields.SFImage(def);
+
+ if (ctx && ctx.xmlNode) { this.initSetter(ctx.xmlNode, name); }
+ this._vfFieldTypes[name] = "SFImage";
+ },
+
+ // multi fields
+ addField_MFString: function (ctx, name, def) {
+ this._vf[name] = ctx && ctx.xmlNode && ctx.xmlNode.hasAttribute(name) ?
+ x3dom.fields.MFString.parse(ctx.xmlNode.getAttribute(name)) :
+ new x3dom.fields.MFString(def);
+
+ if (ctx && ctx.xmlNode) { this.initSetter(ctx.xmlNode, name); }
+ this._vfFieldTypes[name] = "MFString";
+ },
+
+ addField_MFBoolean: function (ctx, name, def) {
+ this._vf[name] = ctx && ctx.xmlNode && ctx.xmlNode.hasAttribute(name) ?
+ x3dom.fields.MFBoolean.parse(ctx.xmlNode.getAttribute(name)) :
+ new x3dom.fields.MFBoolean(def);
+
+ if (ctx && ctx.xmlNode) { this.initSetter(ctx.xmlNode, name); }
+ this._vfFieldTypes[name] = "MFBoolean";
+ },
+
+ addField_MFInt32: function (ctx, name, def) {
+ this._vf[name] = ctx && ctx.xmlNode && ctx.xmlNode.hasAttribute(name) ?
+ x3dom.fields.MFInt32.parse(ctx.xmlNode.getAttribute(name)) :
+ new x3dom.fields.MFInt32(def);
+
+ if (ctx && ctx.xmlNode) { this.initSetter(ctx.xmlNode, name); }
+ this._vfFieldTypes[name] = "MFInt32";
+ },
+
+ addField_MFFloat: function (ctx, name, def) {
+ this._vf[name] = ctx && ctx.xmlNode && ctx.xmlNode.hasAttribute(name) ?
+ x3dom.fields.MFFloat.parse(ctx.xmlNode.getAttribute(name)) :
+ new x3dom.fields.MFFloat(def);
+
+ if (ctx && ctx.xmlNode) { this.initSetter(ctx.xmlNode, name); }
+ this._vfFieldTypes[name] = "MFFloat";
+ },
+
+ addField_MFDouble: function (ctx, name, def) {
+ this._vf[name] = ctx && ctx.xmlNode && ctx.xmlNode.hasAttribute(name) ?
+ x3dom.fields.MFFloat.parse(ctx.xmlNode.getAttribute(name)) :
+ new x3dom.fields.MFFloat(def);
+
+ if (ctx && ctx.xmlNode) { this.initSetter(ctx.xmlNode, name); }
+ this._vfFieldTypes[name] = "MFDouble";
+ },
+
+ addField_MFColor: function (ctx, name, def) {
+ this._vf[name] = ctx && ctx.xmlNode && ctx.xmlNode.hasAttribute(name) ?
+ x3dom.fields.MFColor.parse(ctx.xmlNode.getAttribute(name)) :
+ new x3dom.fields.MFColor(def);
+
+ if (ctx && ctx.xmlNode) { this.initSetter(ctx.xmlNode, name); }
+ this._vfFieldTypes[name] = "MFColor";
+ },
+
+ addField_MFColorRGBA: function (ctx, name, def) {
+ this._vf[name] = ctx && ctx.xmlNode && ctx.xmlNode.hasAttribute(name) ?
+ x3dom.fields.MFColorRGBA.parse(ctx.xmlNode.getAttribute(name)) :
+ new x3dom.fields.MFColorRGBA(def);
+
+ if (ctx && ctx.xmlNode) { this.initSetter(ctx.xmlNode, name); }
+ this._vfFieldTypes[name] = "MFColorRGBA";
+ },
+
+ addField_MFVec2f: function (ctx, name, def) {
+ this._vf[name] = ctx && ctx.xmlNode && ctx.xmlNode.hasAttribute(name) ?
+ x3dom.fields.MFVec2f.parse(ctx.xmlNode.getAttribute(name)) :
+ new x3dom.fields.MFVec2f(def);
+
+ if (ctx && ctx.xmlNode) { this.initSetter(ctx.xmlNode, name); }
+ this._vfFieldTypes[name] = "MFVec2f";
+ },
+
+ addField_MFVec3f: function (ctx, name, def) {
+ this._vf[name] = ctx && ctx.xmlNode && ctx.xmlNode.hasAttribute(name) ?
+ x3dom.fields.MFVec3f.parse(ctx.xmlNode.getAttribute(name)) :
+ new x3dom.fields.MFVec3f(def);
+
+ if (ctx && ctx.xmlNode) { this.initSetter(ctx.xmlNode, name); }
+ this._vfFieldTypes[name] = "MFVec3f";
+ },
+
+ addField_MFVec3d: function (ctx, name, def) {
+ this.addField_MFVec3f(ctx, name, def);
+ this._vfFieldTypes[name] = "MFVec3d";
+ },
+
+ addField_MFRotation: function (ctx, name, def) {
+ this._vf[name] = ctx && ctx.xmlNode && ctx.xmlNode.hasAttribute(name) ?
+ x3dom.fields.MFRotation.parse(ctx.xmlNode.getAttribute(name)) :
+ new x3dom.fields.MFRotation(def);
+
+ if (ctx && ctx.xmlNode) { this.initSetter(ctx.xmlNode, name); }
+ this._vfFieldTypes[name] = "MFRotation";
+ },
+
+ // child node fields
+ addField_SFNode: function (name, type) {
+ this._cf[name] = new x3dom.fields.SFNode(type);
+ this._cfFieldTypes[name] = "SFNode";
+ },
+ addField_MFNode: function (name, type) {
+ this._cf[name] = new x3dom.fields.MFNode(type);
+ this._cfFieldTypes[name] = "MFNode";
+ }
+ }
+ )
+);
+
+/** @namespace x3dom.nodeTypes */
+/*
+ * X3DOM JavaScript Library
+ * http://www.x3dom.org
+ *
+ * (C)2009 Fraunhofer IGD, Darmstadt, Germany
+ * Dual licensed under the MIT and GPL
+ */
+
+/* ### X3DMetadataObject ### */
+x3dom.registerNodeType(
+ "X3DMetadataObject",
+ "Core",
+ defineClass(x3dom.nodeTypes.X3DNode,
+
+ /**
+ * Constructor for X3DMetadataObject
+ * @constructs x3dom.nodeTypes.X3DMetadataObject
+ * @x3d 3.3
+ * @component Core
+ * @status full
+ * @extends x3dom.nodeTypes.X3DNode
+ * @param {Object} [ctx=null] - context object, containing initial settings like namespace
+ * @classdesc This abstract interface is the basis for all metadata nodes. The interface is inherited by
+ * all metadata nodes.
+ */
+ function (ctx) {
+ x3dom.nodeTypes.X3DMetadataObject.superClass.call(this, ctx);
+
+
+ /**
+ * The specification of a non-empty value for the name field is required.
+ * @var {x3dom.fields.SFString} name
+ * @memberof x3dom.nodeTypes.X3DMetadataObject
+ * @initvalue ""
+ * @field x3d
+ * @instance
+ */
+ this.addField_SFString(ctx, 'name', "");
+
+ /**
+ * The specification of the reference field is optional. If provided, it identifies the metadata standard
+ * or other specification that defines the name field. If the reference field is not provided or is empty,
+ * the meaning of the name field is considered implicit to the characters in the string.
+ * @var {x3dom.fields.SFString} reference
+ * @memberof x3dom.nodeTypes.X3DMetadataObject
+ * @initvalue ""
+ * @field x3d
+ * @instance
+ */
+ this.addField_SFString(ctx, 'reference', "");
+
+ }
+ )
+);
+/** @namespace x3dom.nodeTypes */
+/*
+ * X3DOM JavaScript Library
+ * http://www.x3dom.org
+ *
+ * (C)2009 Fraunhofer IGD, Darmstadt, Germany
+ * Dual licensed under the MIT and GPL
+ */
+
+/* ### MetadataBoolean ### */
+x3dom.registerNodeType(
+ "MetadataBoolean",
+ "Core",
+ defineClass(x3dom.nodeTypes.X3DMetadataObject,
+
+ /**
+ * Constructor for MetadataBoolean
+ * @constructs x3dom.nodeTypes.MetadataBoolean
+ * @x3d 3.3
+ * @component Core
+ * @status full
+ * @extends x3dom.nodeTypes.X3DMetadataObject
+ * @param {Object} [ctx=null] - context object, containing initial settings like namespace
+ * @classdesc The metadata provided by this node is contained in the Boolean values of the value field.
+ */
+ function (ctx) {
+ x3dom.nodeTypes.MetadataBoolean.superClass.call(this, ctx);
+
+
+ /**
+ *
+ * @var {x3dom.fields.MFBoolean} value
+ * @memberof x3dom.nodeTypes.MetadataBoolean
+ * @initvalue []
+ * @field x3d
+ * @instance
+ */
+ this.addField_MFBoolean(ctx, 'value', []);
+
+ }
+ )
+);
+/** @namespace x3dom.nodeTypes */
+/*
+ * X3DOM JavaScript Library
+ * http://www.x3dom.org
+ *
+ * (C)2009 Fraunhofer IGD, Darmstadt, Germany
+ * Dual licensed under the MIT and GPL
+ */
+
+/* ### MetadataDouble ### */
+x3dom.registerNodeType(
+ "MetadataDouble",
+ "Core",
+ defineClass(x3dom.nodeTypes.X3DMetadataObject,
+
+ /**
+ * Constructor for MetadataDouble
+ * @constructs x3dom.nodeTypes.MetadataDouble
+ * @x3d 3.3
+ * @component Core
+ * @status full
+ * @extends x3dom.nodeTypes.X3DMetadataObject
+ * @param {Object} [ctx=null] - context object, containing initial settings like namespace
+ * @classdesc The metadata provided by this node is contained in the double-precision floating point numbers of
+ * the value field.
+ */
+ function (ctx) {
+ x3dom.nodeTypes.MetadataDouble.superClass.call(this, ctx);
+
+
+ /**
+ *
+ * @var {x3dom.fields.MFDouble} value
+ * @memberof x3dom.nodeTypes.MetadataDouble
+ * @initvalue []
+ * @field x3d
+ * @instance
+ */
+ this.addField_MFDouble(ctx, 'value', []);
+
+ }
+ )
+);
+/** @namespace x3dom.nodeTypes */
+/*
+ * X3DOM JavaScript Library
+ * http://www.x3dom.org
+ *
+ * (C)2009 Fraunhofer IGD, Darmstadt, Germany
+ * Dual licensed under the MIT and GPL
+ */
+
+/* ### MetadataFloat ### */
+x3dom.registerNodeType(
+ "MetadataFloat",
+ "Core",
+ defineClass(x3dom.nodeTypes.X3DMetadataObject,
+
+ /**
+ * Constructor for MetadataFloat
+ * @constructs x3dom.nodeTypes.MetadataFloat
+ * @x3d 3.3
+ * @component Core
+ * @status full
+ * @extends x3dom.nodeTypes.X3DMetadataObject
+ * @param {Object} [ctx=null] - context object, containing initial settings like namespace
+ * @classdesc The metadata provided by this node is contained in the single-precision floating point numbers of
+ * the value field.
+ */
+ function (ctx) {
+ x3dom.nodeTypes.MetadataFloat.superClass.call(this, ctx);
+
+
+ /**
+ *
+ * @var {x3dom.fields.MFFloat} value
+ * @memberof x3dom.nodeTypes.MetadataFloat
+ * @initvalue []
+ * @field x3d
+ * @instance
+ */
+ this.addField_MFFloat(ctx, 'value', []);
+
+ }
+ )
+);
+/** @namespace x3dom.nodeTypes */
+/*
+ * X3DOM JavaScript Library
+ * http://www.x3dom.org
+ *
+ * (C)2009 Fraunhofer IGD, Darmstadt, Germany
+ * Dual licensed under the MIT and GPL
+ */
+
+/* ### MetadataInteger ### */
+x3dom.registerNodeType(
+ "MetadataInteger",
+ "Core",
+ defineClass(x3dom.nodeTypes.X3DMetadataObject,
+
+ /**
+ * Constructor for MetadataInteger
+ * @constructs x3dom.nodeTypes.MetadataInteger
+ * @x3d 3.3
+ * @component Core
+ * @status full
+ * @extends x3dom.nodeTypes.X3DMetadataObject
+ * @param {Object} [ctx=null] - context object, containing initial settings like namespace
+ * @classdesc The metadata provided by this node is contained in the integers of the value field.
+ */
+ function (ctx) {
+ x3dom.nodeTypes.MetadataInteger.superClass.call(this, ctx);
+
+
+ /**
+ *
+ * @var {x3dom.fields.MFInt32} value
+ * @memberof x3dom.nodeTypes.MetadataInteger
+ * @initvalue []
+ * @field x3dom
+ * @instance
+ */
+ this.addField_MFInt32(ctx, 'value', []);
+
+ }
+ )
+);
+/** @namespace x3dom.nodeTypes */
+/*
+ * X3DOM JavaScript Library
+ * http://www.x3dom.org
+ *
+ * (C)2009 Fraunhofer IGD, Darmstadt, Germany
+ * Dual licensed under the MIT and GPL
+ */
+
+/* ### MetadataSet ### */
+x3dom.registerNodeType(
+ "MetadataSet",
+ "Core",
+ defineClass(x3dom.nodeTypes.X3DMetadataObject,
+
+ /**
+ * Constructor for MetadataSet
+ * @constructs x3dom.nodeTypes.MetadataSet
+ * @x3d 3.3
+ * @component Core
+ * @status full
+ * @extends x3dom.nodeTypes.X3DMetadataObject
+ * @param {Object} [ctx=null] - context object, containing initial settings like namespace
+ * @classdesc The metadata provided by this node is contained in the metadata nodes of the value field.
+ */
+ function (ctx) {
+ x3dom.nodeTypes.MetadataSet.superClass.call(this, ctx);
+
+
+ /**
+ *
+ * @var {x3dom.fields.MFNode} value
+ * @memberof x3dom.nodeTypes.MetadataSet
+ * @initvalue x3dom.nodeTypes.X3DMetadataObject
+ * @field x3d
+ * @instance
+ */
+ this.addField_MFNode('value', x3dom.nodeTypes.X3DMetadataObject);
+
+ }
+ )
+);
+/** @namespace x3dom.nodeTypes */
+/*
+ * X3DOM JavaScript Library
+ * http://www.x3dom.org
+ *
+ * (C)2009 Fraunhofer IGD, Darmstadt, Germany
+ * Dual licensed under the MIT and GPL
+ */
+
+/* ### MetadataString ### */
+x3dom.registerNodeType(
+ "MetadataString",
+ "Core",
+ defineClass(x3dom.nodeTypes.X3DMetadataObject,
+
+ /**
+ * Constructor for MetadataString
+ * @constructs x3dom.nodeTypes.MetadataString
+ * @x3d 3.3
+ * @component Core
+ * @status full
+ * @extends x3dom.nodeTypes.X3DMetadataObject
+ * @param {Object} [ctx=null] - context object, containing initial settings like namespace
+ * @classdesc The metadata provided by this node is contained in the strings of the value field.
+ */
+ function (ctx) {
+ x3dom.nodeTypes.MetadataString.superClass.call(this, ctx);
+
+
+ /**
+ *
+ * @var {x3dom.fields.MFString} value
+ * @memberof x3dom.nodeTypes.MetadataString
+ * @initvalue []
+ * @field x3d
+ * @instance
+ */
+ this.addField_MFString(ctx, 'value', []);
+
+ }
+ )
+);
+/** @namespace x3dom.nodeTypes */
+/*
+ * X3DOM JavaScript Library
+ * http://www.x3dom.org
+ *
+ * (C)2009 Fraunhofer IGD, Darmstadt, Germany
+ * Dual licensed under the MIT and GPL
+ */
+
+/* ### Field ### */
+x3dom.registerNodeType(
+ "Field",
+ "Core",
+ defineClass(x3dom.nodeTypes.X3DNode,
+
+ /**
+ * Constructor for Field
+ * @constructs x3dom.nodeTypes.Field
+ * @x3d x.x
+ * @component Core
+ * @status full
+ * @extends x3dom.nodeTypes.X3DNode
+ * @param {Object} [ctx=null] - context object, containing initial settings like namespace
+ * @classdesc Class represents a field of a node containing name, type and value
+ */
+ function (ctx) {
+ x3dom.nodeTypes.Field.superClass.call(this, ctx);
+
+
+ /**
+ *
+ * @var {x3dom.fields.SFString} name
+ * @memberof x3dom.nodeTypes.Field
+ * @initvalue ""
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFString(ctx, 'name', "");
+
+ /**
+ *
+ * @var {x3dom.fields.SFString} type
+ * @memberof x3dom.nodeTypes.Field
+ * @initvalue ""
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFString(ctx, 'type', "");
+
+ /**
+ *
+ * @var {x3dom.fields.SFString} value
+ * @memberof x3dom.nodeTypes.Field
+ * @initvalue ""
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFString(ctx, 'value', "");
+
+ },
+ {
+ fieldChanged: function(fieldName) {
+ var that = this;
+ if (fieldName === 'value') {
+ Array.forEach(this._parentNodes, function (node) {
+ node.fieldChanged(that._vf.name);
+ });
+ }
+ }
+ }
+ )
+);
+/** @namespace x3dom.nodeTypes */
+/*
+ * X3DOM JavaScript Library
+ * http://www.x3dom.org
+ *
+ * (C)2009 Fraunhofer IGD, Darmstadt, Germany
+ * Dual licensed under the MIT and GPL
+ */
+
+/* ### X3DChildNode ### */
+x3dom.registerNodeType(
+ "X3DChildNode",
+ "Core",
+ defineClass(x3dom.nodeTypes.X3DNode,
+
+ /**
+ * Constructor for X3DChildNode
+ * @constructs x3dom.nodeTypes.X3DChildNode
+ * @x3d 3.3
+ * @component Core
+ * @status full
+ * @extends x3dom.nodeTypes.X3DNode
+ * @param {Object} [ctx=null] - context object, containing initial settings like namespace
+ * @classdesc This abstract node type indicates that the concrete nodes that are instantiated based on it may
+ * be used in children, addChildren, and removeChildren fields.
+ */
+ function (ctx) {
+ x3dom.nodeTypes.X3DChildNode.superClass.call(this, ctx);
+
+ }
+ )
+);
+/** @namespace x3dom.nodeTypes */
+/*
+ * X3DOM JavaScript Library
+ * http://www.x3dom.org
+ *
+ * (C)2009 Fraunhofer IGD, Darmstadt, Germany
+ * Dual licensed under the MIT and GPL
+ */
+
+/* ### X3DBindableNode ### */
+x3dom.registerNodeType(
+ "X3DBindableNode",
+ "Core",
+ defineClass(x3dom.nodeTypes.X3DChildNode,
+
+ /**
+ * Constructor for X3DBindableNode
+ * @constructs x3dom.nodeTypes.X3DBindableNode
+ * @x3d 3.3
+ * @component Core
+ * @status experimental
+ * @extends x3dom.nodeTypes.X3DChildNode
+ * @param {Object} [ctx=null] - context object, containing initial settings like namespace
+ * @classdesc X3DBindableNode is the abstract base type for all bindable children nodes.
+ */
+ function (ctx) {
+ x3dom.nodeTypes.X3DBindableNode.superClass.call(this, ctx);
+
+
+ /**
+ * Pushes/pops the node on/from the top of the bindable stack
+ * @var {x3dom.fields.SFBool} bind
+ * @memberof x3dom.nodeTypes.X3DBindableNode
+ * @initvalue false
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFBool(ctx, 'bind', false);
+
+ /**
+ * Description of the bindable node
+ * @var {x3dom.fields.SFString} description
+ * @memberof x3dom.nodeTypes.X3DBindableNode
+ * @initvalue ""
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFString(ctx, 'description', "");
+
+ /**
+ *
+ * @var {x3dom.fields.SFBool} isActive
+ * @memberof x3dom.nodeTypes.X3DBindableNode
+ * @initvalue false
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFBool(ctx, 'isActive', false);
+
+ this._autoGen = (ctx && ctx.autoGen ? true : false);
+ if (this._autoGen)
+ this._vf.description = "default" + this.constructor.superClass._typeName;
+
+ // Bindable stack to register node later on
+ this._stack = null;
+
+ },
+ {
+ bind: function (value) {
+ if (this._stack) {
+ if (value) {
+ this._stack.push (this);
+ }
+ else {
+ this._stack.pop (this);
+ }
+ }
+ else {
+ x3dom.debug.logError ('No BindStack in ' + this.typeName() + 'Bindable');
+ }
+ },
+
+ activate: function (prev) {
+ this.postMessage('isActive', true);
+ x3dom.debug.logInfo('activate ' + this.typeName() + 'Bindable ' +
+ this._DEF + '/' + this._vf.description);
+ },
+
+ deactivate: function (prev) {
+ this.postMessage('isActive', false);
+ x3dom.debug.logInfo('deactivate ' + this.typeName() + 'Bindable ' +
+ this._DEF + '/' + this._vf.description);
+ },
+
+ fieldChanged: function(fieldName) {
+ if (fieldName.indexOf("bind") >= 0) {
+ this.bind(this._vf.bind);
+ }
+ },
+
+ nodeChanged: function() {
+ this._stack = this._nameSpace.doc._bindableBag.addBindable(this);
+ }
+ }
+ )
+);
+/** @namespace x3dom.nodeTypes */
+/*
+ * X3DOM JavaScript Library
+ * http://www.x3dom.org
+ *
+ * (C)2009 Fraunhofer IGD, Darmstadt, Germany
+ * Dual licensed under the MIT and GPL
+ */
+
+/* ### X3DInfoNode ### */
+x3dom.registerNodeType(
+ "X3DInfoNode",
+ "Core",
+ defineClass(x3dom.nodeTypes.X3DChildNode,
+
+ /**
+ * Constructor for X3DInfoNode
+ * @constructs x3dom.nodeTypes.X3DInfoNode
+ * @x3d 3.3
+ * @component Core
+ * @status full
+ * @extends x3dom.nodeTypes.X3DChildNode
+ * @param {Object} [ctx=null] - context object, containing initial settings like namespace
+ * @classdesc This is the base node type for all nodes that contain only information without visual semantics.
+ */
+ function (ctx) {
+ x3dom.nodeTypes.X3DInfoNode.superClass.call(this, ctx);
+
+ }
+ )
+);
+/** @namespace x3dom.nodeTypes */
+/*
+ * X3DOM JavaScript Library
+ * http://www.x3dom.org
+ *
+ * (C)2009 Fraunhofer IGD, Darmstadt, Germany
+ * Dual licensed under the MIT and GPL
+ */
+
+/* ### WorldInfo ### */
+x3dom.registerNodeType(
+ "WorldInfo",
+ "Core",
+ defineClass(x3dom.nodeTypes.X3DInfoNode,
+
+ /**
+ * Constructor for WorldInfo
+ * @constructs x3dom.nodeTypes.WorldInfo
+ * @x3d 3.3
+ * @component Core
+ * @status full
+ * @extends x3dom.nodeTypes.X3DInfoNode
+ * @param {Object} [ctx=null] - context object, containing initial settings like namespace
+ * @classdesc The WorldInfo node contains information about the world. This node is strictly for documentation
+ * purposes and has no effect on the visual appearance or behaviour of the world.
+ */
+ function (ctx) {
+ x3dom.nodeTypes.WorldInfo.superClass.call(this, ctx);
+
+
+ /**
+ * The title field is intended to store the name or title of the world so that browsers can present this to
+ * the user (perhaps in the window border).
+ * @var {x3dom.fields.MFString} info
+ * @memberof x3dom.nodeTypes.WorldInfo
+ * @initvalue []
+ * @field x3dom
+ * @instance
+ */
+ this.addField_MFString(ctx, 'info', []);
+
+ /**
+ * Information about the world can be stored in the info field, such as author information, copyright, and
+ * usage instructions.
+ * @var {x3dom.fields.SFString} title
+ * @memberof x3dom.nodeTypes.WorldInfo
+ * @initvalue ""
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFString(ctx, 'title', "");
+
+ x3dom.debug.logInfo(this._vf.info);
+ x3dom.debug.logInfo(this._vf.title);
+
+ }
+ )
+);
+/** @namespace x3dom.nodeTypes */
+/*
+ * X3DOM JavaScript Library
+ * http://www.x3dom.org
+ *
+ * (C)2009 Fraunhofer IGD, Darmstadt, Germany
+ * Dual licensed under the MIT and GPL
+ */
+
+// ### X3DSensorNode ###
+x3dom.registerNodeType(
+ "X3DSensorNode",
+ "Core",
+ defineClass(x3dom.nodeTypes.X3DChildNode,
+
+ /**
+ * Constructor for X3DSensorNode
+ * @constructs x3dom.nodeTypes.X3DSensorNode
+ * @x3d 3.3
+ * @component Core
+ * @status experimental
+ * @extends x3dom.nodeTypes.X3DChildNode
+ * @param {Object} [ctx=null] - context object, containing initial settings like namespace
+ * @classdesc This abstract node type is the base type for all sensors.
+ */
+ function (ctx) {
+ x3dom.nodeTypes.X3DSensorNode.superClass.call(this, ctx);
+
+
+ /**
+ * Specifies whether this sensor is enabled. A disabled sensor does not produce any output.
+ * @var {x3dom.fields.SFBool} enabled
+ * @memberof x3dom.nodeTypes.X3DSensorNode
+ * @initvalue true
+ * @field x3d
+ * @instance
+ */
+ this.addField_SFBool(ctx, 'enabled', true);
+
+
+ }
+ )
+);
+/** @namespace x3dom.nodeTypes */
+/*
+ * X3DOM JavaScript Library
+ * http://www.x3dom.org
+ *
+ * (C)2009 Fraunhofer IGD, Darmstadt, Germany
+ * Dual licensed under the MIT and GPL
+ */
+
+// deprecated, will be removed in 1.5
+// ### Param ###
+x3dom.registerNodeType(
+ "Param",
+ "Core",
+ defineClass(x3dom.nodeTypes.X3DNode,
+
+ /**
+ * Constructor for Param
+ * @constructs x3dom.nodeTypes.Param
+ * @x3d x.x
+ * @component Core
+ * @status experimental
+ * @extends x3dom.nodeTypes.X3DNode
+ * @param {Object} [ctx=null] - context object, containing initial settings like namespace
+ * DEPRECATED: Param element needs to be child of X3D element {@link http://x3dom.org/docs/latest/configuration.html}
+ */
+ function (ctx) {
+ x3dom.nodeTypes.Param.superClass.call(this, ctx);
+
+ x3dom.debug.logWarning('DEPRECATED: Param element needs to be child of X3D element '
+ + '[<a href="http://x3dom.org/docs/latest/configuration.html">DOCS</a>]');
+
+ }
+ )
+);
+/** @namespace x3dom.nodeTypes */
+/*
+ * X3DOM JavaScript Library
+ * http://www.x3dom.org
+ *
+ * (C)2009 Fraunhofer IGD, Darmstadt, Germany
+ * Dual licensed under the MIT and GPL
+ */
+
+/* ### X3DBoundedObject ### */
+x3dom.registerNodeType(
+ "X3DBoundedObject",
+ "Grouping",
+ defineClass(x3dom.nodeTypes.X3DChildNode,
+
+ /**
+ * Constructor for X3DBoundedObject
+ * @constructs x3dom.nodeTypes.X3DBoundedObject
+ * @x3d 3.3
+ * @component Grouping
+ * @status full
+ * @extends x3dom.nodeTypes.X3DChildNode
+ * @param {Object} [ctx=null] - context object, containing initial settings like namespace
+ * @classdesc This abstract node type is the basis for all node types that have bounds specified as part of
+ * the definition. The bboxCenter and bboxSize fields specify a bounding box that encloses the grouping node's
+ * children. This is a hint that may be used for optimization purposes.
+ */
+ function (ctx) {
+ x3dom.nodeTypes.X3DBoundedObject.superClass.call(this, ctx);
+
+
+ /**
+ * Flag to enable/disable rendering
+ * @var {x3dom.fields.SFBool} render
+ * @memberof x3dom.nodeTypes.X3DBoundedObject
+ * @initvalue true
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFBool(ctx, 'render', true);
+
+ /**
+ * Center of the bounding box
+ * @var {x3dom.fields.SFVec3f} bboxCenter
+ * @memberof x3dom.nodeTypes.X3DBoundedObject
+ * @initvalue 0,0,0
+ * @range [-inf, inf]
+ * @field x3d
+ * @instance
+ */
+ this.addField_SFVec3f(ctx, 'bboxCenter', 0, 0, 0);
+
+ /**
+ * Size of the bounding box
+ * @var {x3dom.fields.SFVec3f} bboxSize
+ * @memberof x3dom.nodeTypes.X3DBoundedObject
+ * @initvalue -1,-1,-1
+ * @range [0, inf] or -1
+ * @field x3d
+ * @instance
+ */
+ this.addField_SFVec3f(ctx, 'bboxSize', -1, -1, -1);
+
+ this._graph = {
+ boundedNode: this, // backref to node object
+ localMatrix: x3dom.fields.SFMatrix4f.identity(), // usually identity
+ globalMatrix: null, // new x3dom.fields.SFMatrix4f();
+ volume: new x3dom.fields.BoxVolume(), // local bbox
+ worldVolume: new x3dom.fields.BoxVolume(), // global bbox
+ center: new x3dom.fields.SFVec3f(0,0,0), // center in eye coords
+ coverage: -1, // currently approx. number of pixels on screen
+ needCulling: true // to be able to disable culling per node
+ };
+
+ },
+ {
+ fieldChanged: function (fieldName) {
+ // TODO; wait for sync traversal to invalidate en block
+ if (this._vf.hasOwnProperty(fieldName)) {
+ this.invalidateVolume();
+ //this.invalidateCache();
+ }
+ },
+
+ nodeChanged: function () {
+ // TODO; wait for sync traversal to invalidate en block
+ this.invalidateVolume();
+ //this.invalidateCache();
+ },
+
+ parentAdded: function(parent) {
+ // some default behavior if not overwitten
+ this.invalidateVolume();
+ //this.invalidateCache();
+ },
+
+ getVolume: function()
+ {
+ var vol = this._graph.volume;
+
+ if (!this.volumeValid() && this._vf.render)
+ {
+ for (var i=0, n=this._childNodes.length; i<n; i++)
+ {
+ var child = this._childNodes[i];
+ // render could be undefined, but undefined != true
+ if (!child || child._vf.render !== true)
+ continue;
+
+ var childVol = child.getVolume();
+
+ if (childVol && childVol.isValid())
+ vol.extendBounds(childVol.min, childVol.max);
+ }
+ }
+
+ return vol;
+ },
+
+ invalidateVolume: function()
+ {
+ var graph = this._graph;
+
+ graph.volume.invalidate();
+
+ // also clear cache
+ graph.worldVolume.invalidate();
+ graph.globalMatrix = null;
+
+ // set parent volumes invalid, too
+ for (var i=0, n=this._parentNodes.length; i<n; i++) {
+ var node = this._parentNodes[i];
+ if (node && node.volumeValid())
+ node.invalidateVolume();
+ }
+ },
+
+ invalidateCache: function()
+ {
+ var graph = this._graph;
+
+ //if (graph.volume.isValid() &&
+ // graph.globalMatrix == null && !graph.worldVolume.isValid())
+ // return; // stop here, we're already done
+
+ graph.worldVolume.invalidate();
+ graph.globalMatrix = null;
+
+ // clear children's cache, too
+ //for (var i=0, n=this._childNodes.length; i<n; i++) {
+ // var node = this._childNodes[i];
+ // if (node)
+ // node.invalidateCache();
+ //}
+ },
+
+ cacheInvalid: function()
+ {
+ return ( this._graph.globalMatrix == null ||
+ !this._graph.worldVolume.isValid() );
+ },
+
+ volumeValid: function()
+ {
+ return this._graph.volume.isValid();
+ },
+
+ graphState: function()
+ {
+ return this._graph;
+ },
+
+ forceUpdateCoverage: function()
+ {
+ return false;
+ }
+ }
+ )
+);
+/** @namespace x3dom.nodeTypes */
+/*
+ * X3DOM JavaScript Library
+ * http://www.x3dom.org
+ *
+ * (C)2009 Fraunhofer IGD, Darmstadt, Germany
+ * Dual licensed under the MIT and GPL
+ */
+
+// ### X3DGroupingNode ###
+x3dom.registerNodeType(
+ "X3DGroupingNode",
+ "Grouping",
+ defineClass(x3dom.nodeTypes.X3DBoundedObject,
+
+ /**
+ * Constructor for X3DGroupingNode
+ * @constructs x3dom.nodeTypes.X3DGroupingNode
+ * @x3d 3.3
+ * @component Grouping
+ * @status experimental
+ * @extends x3dom.nodeTypes.X3DBoundedObject
+ * @param {Object} [ctx=null] - context object, containing initial settings like namespace
+ * @classdesc This abstract node type indicates that concrete node types derived from it contain children nodes
+ * and is the basis for all aggregation.
+ */
+ function (ctx) {
+ x3dom.nodeTypes.X3DGroupingNode.superClass.call(this, ctx);
+
+
+ /**
+ * Grouping nodes have a field that contains a list of children nodes. Each grouping node defines a
+ * coordinate space for its children. This coordinate space is relative to the coordinate space of the node
+ * of which the group node is a child. Such a node is called a parent node. This means that transformations
+ * accumulate down the scene graph hierarchy.
+ * @var {x3dom.fields.MFNode} children
+ * @memberof x3dom.nodeTypes.X3DGroupingNode
+ * @initvalue X3DChildNode
+ * @field x3d
+ * @instance
+ */
+ this.addField_MFNode('children', x3dom.nodeTypes.X3DChildNode);
+ // FIXME; add addChild and removeChild slots ?
+
+ },
+ {
+ collectDrawableObjects: function (transform, drawableCollection, singlePath, invalidateCache, planeMask, clipPlanes)
+ {
+ // check if multi parent sub-graph, don't cache in that case
+ if (singlePath && (this._parentNodes.length > 1))
+ singlePath = false;
+
+ // an invalid world matrix or volume needs to be invalidated down the hierarchy
+ if (singlePath && (invalidateCache = invalidateCache || this.cacheInvalid()))
+ this.invalidateCache();
+
+ // check if sub-graph can be culled away or render flag was set to false
+ planeMask = drawableCollection.cull(transform, this.graphState(), singlePath, planeMask);
+ if (planeMask <= 0) {
+ return;
+ }
+
+ var cnode, childTransform;
+
+ if (singlePath) {
+ // rebuild cache on change and reuse world transform
+ if (!this._graph.globalMatrix) {
+ this._graph.globalMatrix = this.transformMatrix(transform);
+ }
+ childTransform = this._graph.globalMatrix;
+ }
+ else {
+ childTransform = this.transformMatrix(transform);
+ }
+
+ var n = this._childNodes.length;
+
+ if (x3dom.nodeTypes.ClipPlane.count > 0) {
+ var localClipPlanes = [];
+
+ for (var j = 0; j < n; j++) {
+ if ( (cnode = this._childNodes[j]) ) {
+ if (x3dom.isa(cnode, x3dom.nodeTypes.ClipPlane) && cnode._vf.on && cnode._vf.enabled) {
+ localClipPlanes.push({plane: cnode, trafo: childTransform});
+ }
+ }
+ }
+
+ clipPlanes = localClipPlanes.concat(clipPlanes);
+ }
+
+ for (var i=0; i<n; i++) {
+ if ( (cnode = this._childNodes[i]) ) {
+ cnode.collectDrawableObjects(childTransform, drawableCollection, singlePath, invalidateCache, planeMask, clipPlanes);
+ }
+ }
+ }
+ }
+ )
+);
+/** @namespace x3dom.nodeTypes */
+/*
+ * X3DOM JavaScript Library
+ * http://www.x3dom.org
+ *
+ * (C)2009 Fraunhofer IGD, Darmstadt, Germany
+ * Dual licensed under the MIT and GPL
+ */
+
+// ### Switch ###
+x3dom.registerNodeType(
+ "Switch",
+ "Grouping",
+ defineClass(x3dom.nodeTypes.X3DGroupingNode,
+
+ /**
+ * Constructor for Switch
+ * @constructs x3dom.nodeTypes.Switch
+ * @x3d 3.3
+ * @component Grouping
+ * @status full
+ * @extends x3dom.nodeTypes.X3DGroupingNode
+ * @param {Object} [ctx=null] - context object, containing initial settings like namespace
+ * @classdesc The Switch grouping node traverses zero or one of the nodes specified in the children field.
+ * All nodes under a Switch continue to receive and send events regardless of the value of whichChoice.
+ * For example, if an active TimeSensor is contained within an inactive choice of an Switch, the TimeSensor sends events regardless of the Switch's state.
+ */
+ function (ctx) {
+ x3dom.nodeTypes.Switch.superClass.call(this, ctx);
+
+
+ /**
+ * The whichChoice field specifies the index of the child to traverse, with the first child having index 0.
+ * If whichChoice is less than zero or greater than the number of nodes in the children field, nothing is chosen.
+ * @var {x3dom.fields.SFInt32} whichChoice
+ * @memberof x3dom.nodeTypes.Switch
+ * @initvalue -1
+ * @field x3d
+ * @instance
+ */
+ this.addField_SFInt32(ctx, 'whichChoice', -1);
+
+ },
+ {
+ fieldChanged: function (fieldName) {
+ if (fieldName == "whichChoice") {
+ this.invalidateVolume();
+ //this.invalidateCache();
+ }
+ },
+
+ getVolume: function()
+ {
+ var vol = this._graph.volume;
+
+ if (!this.volumeValid() && this._vf.render)
+ {
+ if (this._vf.whichChoice >= 0 &&
+ this._vf.whichChoice < this._childNodes.length)
+ {
+ var child = this._childNodes[this._vf.whichChoice];
+
+ var childVol = (child && child._vf.render === true) ? child.getVolume() : null;
+
+ if (childVol && childVol.isValid())
+ vol.extendBounds(childVol.min, childVol.max);
+ }
+ }
+
+ return vol;
+ },
+
+ collectDrawableObjects: function (transform, drawableCollection, singlePath, invalidateCache, planeMask, clipPlanes)
+ {
+ if (singlePath && (this._parentNodes.length > 1))
+ singlePath = false;
+
+ if (singlePath && (invalidateCache = invalidateCache || this.cacheInvalid()))
+ this.invalidateCache();
+
+ if (this._vf.whichChoice < 0 || this._vf.whichChoice >= this._childNodes.length ||
+ (planeMask = drawableCollection.cull(transform, this.graphState(), singlePath, planeMask)) <= 0) {
+ return;
+ }
+
+ var cnode, childTransform;
+
+ if (singlePath) {
+ if (!this._graph.globalMatrix) {
+ this._graph.globalMatrix = this.transformMatrix(transform);
+ }
+ childTransform = this._graph.globalMatrix;
+ }
+ else {
+ childTransform = this.transformMatrix(transform);
+ }
+
+ if ( (cnode = this._childNodes[this._vf.whichChoice]) ) {
+ cnode.collectDrawableObjects(childTransform, drawableCollection, singlePath, invalidateCache, planeMask, clipPlanes);
+ }
+ },
+
+ doIntersect: function(line)
+ {
+ if (this._vf.whichChoice < 0 ||
+ this._vf.whichChoice >= this._childNodes.length) {
+ return false;
+ }
+
+ var child = this._childNodes[this._vf.whichChoice];
+ if (child) {
+ return child.doIntersect(line);
+ }
+
+ return false;
+ }
+ }
+ )
+);
+/** @namespace x3dom.nodeTypes */
+/*
+ * X3DOM JavaScript Library
+ * http://www.x3dom.org
+ *
+ * (C)2009 Fraunhofer IGD, Darmstadt, Germany
+ * Dual licensed under the MIT and GPL
+ */
+
+// ### X3DTransformNode ###
+x3dom.registerNodeType(
+ "X3DTransformNode",
+ "Grouping",
+ defineClass(x3dom.nodeTypes.X3DGroupingNode,
+
+ /**
+ * Constructor for X3DTransformNode
+ * @constructs x3dom.nodeTypes.X3DTransformNode
+ * @x3d x.x
+ * @component Grouping
+ * @extends x3dom.nodeTypes.X3DGroupingNode
+ * @param {Object} [ctx=null] - context object, containing initial settings like namespace
+ * @classdesc This abstract node type is the basis for all node types that group and transform their children.
+ */
+ function (ctx) {
+ x3dom.nodeTypes.X3DTransformNode.superClass.call(this, ctx);
+
+ if (ctx)
+ ctx.doc._nodeBag.trans.push(this);
+ else
+ x3dom.debug.logWarning("X3DTransformNode: No runtime context found!");
+
+ // holds the current matrix (local space transform)
+ this._trafo = null;
+
+ // workaround, only check on init if getStyle is necessary, since expensive
+ this._needCssStyleUpdates = true;
+
+ },
+ {
+ tick: function (t)
+ {
+ var dom = this._xmlNode;
+
+ if (dom && (dom['ontransform'] ||
+ dom.hasAttribute('ontransform') ||
+ this._listeners['transform'])) {
+ var transMatrix = this.getCurrentTransform();
+
+ var event = {
+ target: dom,
+ type: 'transform',
+ worldX: transMatrix._03,
+ worldY: transMatrix._13,
+ worldZ: transMatrix._23,
+ cancelBubble: false,
+ stopPropagation: function () {
+ this.cancelBubble = true;
+ }
+ };
+
+ this.callEvtHandler("ontransform", event);
+ }
+
+ // temporary per frame update method for CSS-Transform
+ if (this._needCssStyleUpdates && dom) {
+ var trans = x3dom.getStyle(dom, "-webkit-transform") ||
+ x3dom.getStyle(dom, "-moz-transform") ||
+ x3dom.getStyle(dom, "-ms-transform") ||
+ x3dom.getStyle(dom, "transform");
+
+ if (trans && (trans != 'none')) {
+ this._trafo.setValueByStr(trans);
+
+ this.invalidateVolume();
+ //this.invalidateCache();
+
+ return true;
+ }
+ this._needCssStyleUpdates = false; // no special CSS set
+ }
+
+ return false;
+ },
+
+ transformMatrix: function(transform) {
+ return transform.mult(this._trafo);
+ },
+
+ getVolume: function()
+ {
+ var vol = this._graph.volume;
+
+ if (!this.volumeValid() && this._vf.render)
+ {
+ this._graph.localMatrix = this._trafo;
+
+ for (var i=0, n=this._childNodes.length; i<n; i++)
+ {
+ var child = this._childNodes[i];
+ if (!child || child._vf.render !== true)
+ continue;
+
+ var childVol = child.getVolume();
+
+ if (childVol && childVol.isValid())
+ vol.extendBounds(childVol.min, childVol.max);
+ }
+
+ if (vol.isValid())
+ vol.transform(this._trafo);
+ }
+
+ return vol;
+ },
+
+ doIntersect: function(line)
+ {
+ var isect = false;
+ var mat = this._trafo.inverse();
+
+ var tmpPos = new x3dom.fields.SFVec3f(line.pos.x, line.pos.y, line.pos.z);
+ var tmpDir = new x3dom.fields.SFVec3f(line.dir.x, line.dir.y, line.dir.z);
+
+ line.pos = mat.multMatrixPnt(line.pos);
+ line.dir = mat.multMatrixVec(line.dir);
+
+ if (line.hitObject) {
+ line.dist *= line.dir.length();
+ }
+
+ // check for _nearest_ hit object and don't stop on first!
+ for (var i=0; i<this._childNodes.length; i++)
+ {
+ if (this._childNodes[i]) {
+ isect = this._childNodes[i].doIntersect(line) || isect;
+ }
+ }
+
+ line.pos.setValues(tmpPos);
+ line.dir.setValues(tmpDir);
+
+ if (isect) {
+ line.hitPoint = this._trafo.multMatrixPnt(line.hitPoint);
+ line.dist *= line.dir.length();
+ }
+
+ return isect;
+ },
+
+ parentRemoved: function(parent)
+ {
+ var i, n;
+
+ if (this._parentNodes.length == 0) {
+ var doc = this.findX3DDoc();
+
+ for (i=0, n=doc._nodeBag.trans.length; i<n; i++) {
+ if (doc._nodeBag.trans[i] === this) {
+ doc._nodeBag.trans.splice(i, 1);
+ }
+ }
+ }
+
+ for (i=0, n=this._childNodes.length; i<n; i++) {
+ if (this._childNodes[i]) {
+ this._childNodes[i].parentRemoved(this);
+ }
+ }
+ }
+ }
+ )
+);
+/** @namespace x3dom.nodeTypes */
+/*
+ * X3DOM JavaScript Library
+ * http://www.x3dom.org
+ *
+ * (C)2009 Fraunhofer IGD, Darmstadt, Germany
+ * Dual licensed under the MIT and GPL
+ */
+
+// ### Transform ###
+x3dom.registerNodeType(
+ "Transform",
+ "Grouping",
+ defineClass(x3dom.nodeTypes.X3DTransformNode,
+
+ /**
+ * Constructor for Transform
+ * @constructs x3dom.nodeTypes.Transform
+ * @x3d 3.3
+ * @component Grouping
+ * @status experimental
+ * @extends x3dom.nodeTypes.X3DTransformNode
+ * @param {Object} [ctx=null] - context object, containing initial settings like namespace
+ * @classdesc The Transform node is a grouping node that defines a coordinate system for its children that is relative to the coordinate systems of its ancestors.
+ * The translation, rotation, scale, scaleOrientation and center fields define a geometric 3D transformation.
+ */
+ function (ctx) {
+ x3dom.nodeTypes.Transform.superClass.call(this, ctx);
+
+
+ /**
+ * The center field specifies a translation offset from the origin of the local coordinate system (0,0,0).
+ * @var {x3dom.fields.SFVec3f} center
+ * @memberof x3dom.nodeTypes.Transform
+ * @initvalue 0,0,0
+ * @field x3d
+ * @instance
+ */
+ this.addField_SFVec3f(ctx, 'center', 0, 0, 0);
+
+ /**
+
+ * The translation field specifies a translation to the coordinate system.
+ * @var {x3dom.fields.SFVec3f} translation
+ * @memberof x3dom.nodeTypes.Transform
+ * @initvalue 0,0,0
+ * @field x3d
+ * @instance
+ */
+ this.addField_SFVec3f(ctx, 'translation', 0, 0, 0);
+
+ /**
+ * The rotation field specifies a rotation of the coordinate system.
+ * @var {x3dom.fields.SFRotation} rotation
+ * @memberof x3dom.nodeTypes.Transform
+ * @initvalue 0,0,1,0
+ * @field x3d
+ * @instance
+ */
+ this.addField_SFRotation(ctx, 'rotation', 0, 0, 1, 0);
+
+ /**
+ * The scale field specifies a non-uniform scale of the coordinate system.
+ * Scale values may have any value: positive, negative (indicating a reflection), or zero. A value of zero indicates that any child geometry shall not be displayed.
+ * @var {x3dom.fields.SFVec3f} scale
+ * @memberof x3dom.nodeTypes.Transform
+ * @initvalue 1,1,1
+ * @field x3d
+ * @instance
+ */
+ this.addField_SFVec3f(ctx, 'scale', 1, 1, 1);
+
+ /**
+ * The scaleOrientation specifies a rotation of the coordinate system before the scale (to specify scales in arbitrary orientations).
+ * The scaleOrientation applies only to the scale operation.
+ * @var {x3dom.fields.SFRotation} scaleOrientation
+ * @memberof x3dom.nodeTypes.Transform
+ * @initvalue 0,0,1,0
+ * @field x3d
+ * @instance
+ */
+ this.addField_SFRotation(ctx, 'scaleOrientation', 0, 0, 1, 0);
+
+ // P' = T * C * R * SR * S * -SR * -C * P
+ this._trafo = x3dom.fields.SFMatrix4f.translation(
+ this._vf.translation.add(this._vf.center)).
+ mult(this._vf.rotation.toMatrix()).
+ mult(this._vf.scaleOrientation.toMatrix()).
+ mult(x3dom.fields.SFMatrix4f.scale(this._vf.scale)).
+ mult(this._vf.scaleOrientation.toMatrix().inverse()).
+ mult(x3dom.fields.SFMatrix4f.translation(this._vf.center.negate()));
+
+ },
+ {
+ fieldChanged: function (fieldName)
+ {
+ if (fieldName == "center" || fieldName == "translation" ||
+ fieldName == "rotation" || fieldName == "scale" ||
+ fieldName == "scaleOrientation")
+ {
+ // P' = T * C * R * SR * S * -SR * -C * P
+ this._trafo = x3dom.fields.SFMatrix4f.translation(
+ this._vf.translation.add(this._vf.center)).
+ mult(this._vf.rotation.toMatrix()).
+ mult(this._vf.scaleOrientation.toMatrix()).
+ mult(x3dom.fields.SFMatrix4f.scale(this._vf.scale)).
+ mult(this._vf.scaleOrientation.toMatrix().inverse()).
+ mult(x3dom.fields.SFMatrix4f.translation(this._vf.center.negate()));
+
+ this.invalidateVolume();
+ //this.invalidateCache();
+ }
+ else if (fieldName == "render") {
+ this.invalidateVolume();
+ //this.invalidateCache();
+ }
+ }
+ }
+ )
+);
+/** @namespace x3dom.nodeTypes */
+/*
+ * X3DOM JavaScript Library
+ * http://www.x3dom.org
+ *
+ * (C)2009 Fraunhofer IGD, Darmstadt, Germany
+ * Dual licensed under the MIT and GPL
+ */
+
+// ### MatrixTransform ###
+x3dom.registerNodeType(
+ "MatrixTransform",
+ "Grouping",
+ defineClass(x3dom.nodeTypes.X3DTransformNode,
+
+ /**
+ * Constructor for MatrixTransform
+ * @constructs x3dom.nodeTypes.MatrixTransform
+ * @x3d x.x
+ * @component Grouping
+ * @extends x3dom.nodeTypes.X3DTransformNode
+ * @param {Object} [ctx=null] - context object, containing initial settings like namespace
+ * @classdesc The MatrixTransform node is a grouping node that defines a coordinate system for its children that is relative to the coordinate systems of its ancestors.
+ * The transformation is given as a matrix.
+ */
+ function (ctx) {
+ x3dom.nodeTypes.MatrixTransform.superClass.call(this, ctx);
+
+
+ /**
+ * Defines the transformation matrix.
+ * @var {x3dom.fields.SFMatrix4f} matrix
+ * @memberof x3dom.nodeTypes.MatrixTransform
+ * @initvalue 1,0,0,0
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFMatrix4f(ctx, 'matrix',
+ 1, 0, 0, 0,
+ 0, 1, 0, 0,
+ 0, 0, 1, 0,
+ 0, 0, 0, 1);
+ this._trafo = this._vf.matrix.transpose();
+
+ },
+ {
+ fieldChanged: function (fieldName) {
+ if (fieldName == "matrix") {
+ this._trafo = this._vf.matrix.transpose();
+
+ this.invalidateVolume();
+ //this.invalidateCache();
+ }
+ else if (fieldName == "render") {
+ this.invalidateVolume();
+ //this.invalidateCache();
+ }
+ }
+ }
+ )
+);
+/** @namespace x3dom.nodeTypes */
+/*
+ * X3DOM JavaScript Library
+ * http://www.x3dom.org
+ *
+ * (C)2009 Fraunhofer IGD, Darmstadt, Germany
+ * Dual licensed under the MIT and GPL
+ */
+
+// ### Group ###
+x3dom.registerNodeType(
+ "Group",
+ "Grouping",
+ defineClass(x3dom.nodeTypes.X3DGroupingNode,
+
+ /**
+ * Constructor for Group
+ * @constructs x3dom.nodeTypes.Group
+ * @x3d 3.3
+ * @component Grouping
+ * @status full
+ * @extends x3dom.nodeTypes.X3DGroupingNode
+ * @param {Object} [ctx=null] - context object, containing initial settings like namespace
+ * @classdesc A Group node contains children nodes without introducing a new transformation.
+ * It is equivalent to a Transform node containing an identity transform.
+ */
+ function (ctx) {
+ x3dom.nodeTypes.Group.superClass.call(this, ctx);
+
+ }
+ )
+);
+/** @namespace x3dom.nodeTypes */
+/*
+ * X3DOM JavaScript Library
+ * http://www.x3dom.org
+ *
+ * (C)2009 Fraunhofer IGD, Darmstadt, Germany
+ * Dual licensed under the MIT and GPL
+ */
+
+// ### Block ###
+x3dom.registerNodeType(
+ "Block",
+ "Grouping",
+ defineClass(x3dom.nodeTypes.X3DGroupingNode,
+
+ /**
+ * Constructor for Block
+ * @constructs x3dom.nodeTypes.Block
+ * @x3d x.x
+ * @component Grouping
+ * @extends x3dom.nodeTypes.X3DGroupingNode
+ * @param {Object} [ctx=null] - context object, containing initial settings like namespace
+ */
+ function (ctx) {
+ x3dom.nodeTypes.Block.superClass.call(this, ctx);
+
+
+ /**
+ *
+ * @var {x3dom.fields.MFString} nameSpaceName
+ * @memberof x3dom.nodeTypes.Block
+ * @initvalue []
+ * @field x3dom
+ * @instance
+ */
+ this.addField_MFString(ctx, 'nameSpaceName', []);
+
+ }
+ )
+);
+/** @namespace x3dom.nodeTypes */
+/*
+ * X3DOM JavaScript Library
+ * http://www.x3dom.org
+ *
+ * (C)2009 Fraunhofer IGD, Darmstadt, Germany
+ * Dual licensed under the MIT and GPL
+ */
+
+// ### StaticGroup ###
+x3dom.registerNodeType(
+ "StaticGroup",
+ "Grouping",
+ defineClass(x3dom.nodeTypes.X3DGroupingNode,
+
+ /**
+ * Constructor for StaticGroup
+ * @constructs x3dom.nodeTypes.StaticGroup
+ * @x3d 3.3
+ * @component Grouping
+ * @status full
+ * @extends x3dom.nodeTypes.X3DGroupingNode
+ * @param {Object} [ctx=null] - context object, containing initial settings like namespace
+ * @classdesc The StaticGroup node contains children nodes which cannot be modified.
+ * StaticGroup children are guaranteed to not change, send events, receive events or contain any USE references outside the StaticGroup.
+ * This allows the browser to optimize this content for faster rendering and less memory usage.
+ */
+ function (ctx) {
+ x3dom.nodeTypes.StaticGroup.superClass.call(this, ctx);
+
+ // Node implements optimizations; no need to maintain the children node's
+ // X3D representations, as they cannot be accessed after creation time
+ x3dom.debug.logWarning("StaticGroup erroneously also bakes parent transforms, if happens use Group node!"); // Blender exports to SG
+
+ /**
+ * Enables debugging.
+ * @var {x3dom.fields.SFBool} debug
+ * @memberof x3dom.nodeTypes.StaticGroup
+ * @initvalue false
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFBool(ctx, 'debug', false);
+
+ /**
+ * Enable debug box volumes.
+ * @var {x3dom.fields.SFBool} showDebugBoxVolumes
+ * @memberof x3dom.nodeTypes.StaticGroup
+ * @initvalue false
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFBool(ctx, 'showDebugBoxVolumes', false);
+
+ // type of bvh to use, supported are 'jsBIH', 'BIH' and 'OCTREE'
+
+ /**
+ * Defines the type of bvh to use. Supported are 'jsBIH', 'BIH' and 'OCTREE'.
+ * @var {x3dom.fields.SFString} bvhType
+ * @memberof x3dom.nodeTypes.StaticGroup
+ * @initvalue 'jsBIH'
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFString(ctx, 'bvhType', 'jsBIH');
+
+ /**
+ *
+ * @var {x3dom.fields.SFInt32} maxObjectsPerNode
+ * @memberof x3dom.nodeTypes.StaticGroup
+ * @initvalue 1
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFInt32(ctx, 'maxObjectsPerNode', 1);
+ // -1 sets default values, other values forces maxDepth
+
+ /**
+ *
+ * @var {x3dom.fields.SFInt32} maxDepth
+ * @memberof x3dom.nodeTypes.StaticGroup
+ * @initvalue -1
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFInt32(ctx, 'maxDepth', -1);
+
+ /**
+ *
+ * @var {x3dom.fields.SFFloat} minRelativeBBoxSize
+ * @memberof x3dom.nodeTypes.StaticGroup
+ * @initvalue 0.01
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFFloat(ctx, 'minRelativeBBoxSize', 0.01);
+
+ this.needBvhRebuild = true;
+ this.drawableCollection = null;
+ this.bvh = null;
+
+ },
+ {
+ getMaxDepth : function()
+ {
+ if(this._vf.maxDepth == -1 )
+ {
+ return (this._vf.bvhType == ('jsBIH' || 'BIH')) ? 50 : 4;
+ }
+ return this._vf.maxDepth;
+ },
+
+ collectDrawableObjects: function (transform, drawableCollection, singlePath, invalidateCache, planeMask, clipPlanes)
+ {
+ // check if multi parent sub-graph, don't cache in that case
+ if (singlePath && (this._parentNodes.length > 1))
+ singlePath = false;
+
+ // an invalid world matrix or volume needs to be invalidated down the hierarchy
+ if (singlePath && (invalidateCache = invalidateCache || this.cacheInvalid()))
+ this.invalidateCache();
+
+ // check if sub-graph can be culled away or render flag was set to false
+ planeMask = drawableCollection.cull(transform, this.graphState(), singlePath, planeMask);
+ if (planeMask <= 0) {
+ return;
+ }
+
+ var cnode, childTransform;
+
+ if (singlePath) {
+ // rebuild cache on change and reuse world transform
+ if (!this._graph.globalMatrix) {
+ this._graph.globalMatrix = this.transformMatrix(transform);
+ }
+ childTransform = this._graph.globalMatrix;
+ }
+ else {
+ childTransform = this.transformMatrix(transform);
+ }
+
+ if (this.needBvhRebuild)
+ {
+ var drawableCollectionConfig = {
+ viewArea: drawableCollection.viewarea,
+ sortTrans: drawableCollection.sortTrans,
+ viewMatrix: drawableCollection.viewMatrix,
+ projMatrix: drawableCollection.projMatrix,
+ sceneMatrix: drawableCollection.sceneMatrix,
+ frustumCulling: false,
+ smallFeatureThreshold: 0,//1, // THINKABOUTME
+ context: drawableCollection.context
+ };
+
+ this.drawableCollection = new x3dom.DrawableCollection(drawableCollectionConfig);
+
+ var i, n = this._childNodes.length;
+ for (i=0; i<n; i++) {
+ if ( (cnode = this._childNodes[i]) ) {
+ //this is only used to collect all drawables once
+ cnode.collectDrawableObjects(childTransform, this.drawableCollection, singlePath, invalidateCache, planeMask, clipPlanes);
+ }
+ }
+ this.drawableCollection.concat();
+
+ var scene = this._nameSpace.doc._scene;
+
+ //create settings
+ var bvhSettings = new x3dom.bvh.Settings(
+ this._vf.debug,
+ this._vf.showDebugBoxVolumes,
+ this._vf.bvhType,
+ this._vf.maxObjectsPerNode,
+ this.getMaxDepth(),
+ this._vf.minRelativeBBoxSize
+ );
+ //create bvh type
+ this.bvh = (this._vf.bvhType == 'jsBIH' ) ?
+ new x3dom.bvh.BIH(scene, bvhSettings) :
+ new x3dom.bvh.Culler(this.drawableCollection,scene, bvhSettings);
+
+ //add decorator for debug shapes
+ if(this._vf.debug || this._vf.showDebugBoxVolumes)
+ this.bvh = new x3dom.bvh.DebugDecorator(this.bvh, scene, bvhSettings);
+
+ //add drawables
+ n = this.drawableCollection.length;
+ for (i = 0; i < n; i++)
+ {
+ this.bvh.addDrawable(this.drawableCollection.get(i))
+ }
+
+ //compile bvh
+ this.bvh.compile();
+
+ if(this._vf.debug)
+ this.bvh.showCompileStats();
+
+ this.needBvhRebuild = false; // TODO: re-evaluate if Inline node is child node
+ }
+
+ x3dom.Utils.startMeasure('bvhTraverse');
+ //collect drawables
+ this.bvh.collectDrawables(drawableCollection);
+ var dt = x3dom.Utils.stopMeasure('bvhTraverse');
+ this._nameSpace.doc.ctx.x3dElem.runtime.addMeasurement('BVH', dt);
+
+ //show stats
+ this.bvh.showTraverseStats(this._nameSpace.doc.ctx.x3dElem.runtime);
+ }
+ }
+ )
+);
+/** @namespace x3dom.nodeTypes */
+/*
+ * X3DOM JavaScript Library
+ * http://www.x3dom.org
+ *
+ * (C)2009 Fraunhofer IGD, Darmstadt, Germany
+ * Dual licensed under the MIT and GPL
+ */
+
+// ### RemoteSelectionGroup ###
+x3dom.registerNodeType(
+ "RemoteSelectionGroup",
+ "Grouping",
+ defineClass(x3dom.nodeTypes.X3DGroupingNode,
+
+ /**
+ * Constructor for RemoteSelectionGroup
+ * @constructs x3dom.nodeTypes.RemoteSelectionGroup
+ * @x3d x.x
+ * @component Grouping
+ * @extends x3dom.nodeTypes.X3DGroupingNode
+ * @param {Object} [ctx=null] - context object, containing initial settings like namespace
+ * @classdesc The RemoteSelectionGroup node uses a WebSocket connection to request the results of a a side
+ * culling.
+ */
+ function (ctx) {
+ x3dom.nodeTypes.RemoteSelectionGroup.superClass.call(this, ctx);
+
+
+ /**
+ * The address for the WebSocket connection
+ * @var {x3dom.fields.MFString} url
+ * @memberof x3dom.nodeTypes.RemoteSelectionGroup
+ * @initvalue ["ws://localhost:35668/cstreams/0"]
+ * @field x3dom
+ * @instance
+ */
+ this.addField_MFString(ctx, 'url', ["ws://localhost:35668/cstreams/0"]);
+
+ /**
+ * Defines a list of subsequent id/object pairs.
+ * @var {x3dom.fields.MFString} label
+ * @memberof x3dom.nodeTypes.RemoteSelectionGroup
+ * @initvalue []
+ * @field x3dom
+ * @instance
+ */
+ this.addField_MFString(ctx, 'label', []);
+
+ /**
+ * Sets the maximum number of items that are rendered.
+ * @var {x3dom.fields.SFInt32} maxRenderedIds
+ * @range -1 or [0, inf]
+ * @memberof x3dom.nodeTypes.RemoteSelectionGroup
+ * @initvalue -1
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFInt32(ctx, 'maxRenderedIds', -1);
+
+ /**
+ * Sets whether a reconnect is attempted on a connection loss.
+ * @var {x3dom.fields.SFBool} reconnect
+ * @memberof x3dom.nodeTypes.RemoteSelectionGroup
+ * @initvalue true
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFBool(ctx, 'reconnect', true);
+
+ /**
+ * Sets the scaling factor to reduce the number of render calls during navigation
+ * @var {x3dom.fields.SFFloat} scaleRenderedIdsOnMove
+ * @range [0, 1]
+ * @memberof x3dom.nodeTypes.RemoteSelectionGroup
+ * @initvalue 1.0
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFFloat(ctx, 'scaleRenderedIdsOnMove', 1.0);
+
+ /**
+ * Defines whether culling is used. If culling is disabled the RemoteSelectionGroup works like a normal group.
+ * @var {x3dom.fields.SFBool} enableCulling
+ * @memberof x3dom.nodeTypes.RemoteSelectionGroup
+ * @initvalue true
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFBool(ctx, 'enableCulling', true);
+
+ /**
+ * Defines a set of labels to disable nodes. The label must include the prefix.
+ * @var {x3dom.fields.MFString} invisibleNodes
+ * @memberof x3dom.nodeTypes.RemoteSelectionGroup
+ * @initvalue []
+ * @field x3dom
+ * @instance
+ */
+ this.addField_MFString(ctx, 'invisibleNodes', []);
+
+ this._idList = []; // to be updated by socket connection
+ this._websocket = null; // pointer to socket
+
+ this._nameObjMap = {};
+ this._createTime = [];
+ this._visibleList = [];
+
+ if (ctx)
+ this.initializeSocket(); // init socket connection
+ else
+ x3dom.debug.logWarning("RemoteSelectionGroup: No runtime context found!");
+
+ },
+ {
+ initializeSocket: function()
+ {
+ var that = this;
+
+ if ("WebSocket" in window)
+ {
+ var wsUrl = "ws://localhost:35668/cstreams/0";
+
+ if (this._vf.url.length && this._vf.url[0].length)
+ wsUrl = this._vf.url[0];
+
+ this._websocket = new WebSocket(wsUrl);
+
+ this._websocket._lastMsg = null;
+ this._websocket._lastData = "";
+
+ this._websocket.onopen = function(evt)
+ {
+ x3dom.debug.logInfo("WS Connected");
+
+ var view = that._nameSpace.doc._viewarea.getViewMatrix();
+ this._lastMsg = view.toGL().toString();
+
+ view = that._nameSpace.doc._viewarea.getProjectionMatrix();
+ this._lastMsg += ("," + view.toGL().toString());
+
+ this.send(this._lastMsg);
+ x3dom.debug.logInfo("WS Sent: " + this._lastMsg);
+
+ this._lastMsg = ""; // triggers first update
+ this._lastData = "";
+ };
+
+ this._websocket.onclose = function(evt)
+ {
+ x3dom.debug.logInfo("WS Disconnected");
+
+ if (that._vf.reconnect)
+ {
+ window.setTimeout(function() {
+ that.initializeSocket();
+ }, 2000);
+ }
+ };
+
+ this._websocket.onmessage = function(evt)
+ {
+ if (that._vf.maxRenderedIds < 0)
+ {
+ // render all sent items
+ that._idList = x3dom.fields.MFString.parse(evt.data);
+ }
+ else if (that._vf.maxRenderedIds > 0)
+ {
+ // render #maxRenderedIds items
+ that._idList = [];
+ var arr = x3dom.fields.MFString.parse(evt.data);
+ var n = Math.min(arr.length, Math.abs(that._vf.maxRenderedIds));
+
+ for (var i=0; i<n; ++i) {
+ that._idList[i] = arr[i];
+ }
+ }
+
+ if (that._vf.maxRenderedIds != 0 && this._lastData != evt.data)
+ {
+ this._lastData = evt.data;
+ that._nameSpace.doc.needRender = true;
+ //x3dom.debug.logInfo("WS Response: " + evt.data);
+
+ that.invalidateVolume();
+ //that.invalidateCache();
+ }
+ };
+
+ this._websocket.onerror = function(evt)
+ {
+ x3dom.debug.logError(evt.data);
+ };
+
+ this._websocket.updateCamera = function()
+ {
+ // send again
+ var view = that._nameSpace.doc._viewarea.getViewMatrix();
+ var message = view.toGL().toString();
+
+ view = that._nameSpace.doc._viewarea.getProjectionMatrix();
+ message += ("," + view.toGL().toString());
+
+ if (this._lastMsg != null && this._lastMsg != message)
+ {
+ this._lastMsg = message;
+ this.send(message);
+ //x3dom.debug.logInfo("WS Sent: " + message);
+ }
+ };
+
+ // if there were a d'tor this would belong there
+ // this._websocket.close();
+ }
+ else
+ {
+ x3dom.debug.logError("Browser has no WebSocket support!");
+ }
+ },
+
+ nodeChanged: function ()
+ {
+ var n = this._vf.label.length;
+
+ this._nameObjMap = {};
+ this._createTime = new Array(n);
+ this._visibleList = new Array(n);
+
+ for (var i=0; i<n; ++i)
+ {
+ var shape = this._childNodes[i];
+
+ if (shape && x3dom.isa(shape, x3dom.nodeTypes.X3DShapeNode))
+ {
+ this._nameObjMap[this._vf.label[i]] = { shape: shape, pos: i };
+ this._visibleList[i] = true;
+ }
+ else {
+ this._visibleList[i] = false;
+ x3dom.debug.logError("Invalid children: " + this._vf.label[i]);
+ }
+
+ // init list that holds creation time of gl object
+ this._createTime[i] = 0;
+ }
+
+ this.invalidateVolume();
+ //this.invalidateCache();
+
+ x3dom.debug.logInfo("RemoteSelectionGroup has " + n + " entries.");
+ },
+
+ fieldChanged: function(fieldName)
+ {
+ if (fieldName == "url")
+ {
+ if (this._websocket) {
+ this._websocket.close();
+ this._websocket = null;
+ }
+ this.initializeSocket();
+ }
+ else if (fieldName == "invisibleNodes")
+ {
+ for (var i=0, n=this._vf.label.length; i<n; ++i)
+ {
+ var shape = this._childNodes[i];
+
+ if (shape && x3dom.isa(shape, x3dom.nodeTypes.X3DShapeNode))
+ {
+ this._visibleList[i] = true;
+
+ for (var j=0, numInvis=this._vf.invisibleNodes.length; j<numInvis; ++j)
+ {
+ var nodeName = this._vf.invisibleNodes[j];
+ var starInd = nodeName.lastIndexOf('*');
+ var matchNameBegin = false;
+
+ if (starInd > 0) {
+ nodeName = nodeName.substring(0, starInd);
+ matchNameBegin = true;
+ }
+ if (nodeName.length <= 1)
+ continue;
+
+ if ((matchNameBegin && this._vf.label[i].indexOf(nodeName) == 0) ||
+ this._vf.label[i] == nodeName) {
+ this._visibleList[i] = false;
+ break;
+ }
+ }
+ }
+ else {
+ this._visibleList[i] = false;
+ }
+ }
+
+ this.invalidateVolume();
+ //this.invalidateCache();
+ }
+ else if (fieldName == "render") {
+ this.invalidateVolume();
+ //this.invalidateCache();
+ }
+ },
+
+ getNumRenderedObjects: function(len, isMoving)
+ {
+ var n = len;
+
+ if (this._vf.maxRenderedIds > 0)
+ {
+ var num = Math.max(this._vf.maxRenderedIds, 16); // set lower bound
+
+ var scale = 1; // scale down on move
+ if (isMoving)
+ scale = Math.min(this._vf.scaleRenderedIdsOnMove, 1);
+
+ num = Math.max(Math.round(scale * num), 0);
+ n = Math.min(n, num);
+ }
+
+ return n;
+ },
+
+ collectDrawableObjects: function (transform, drawableCollection, singlePath, invalidateCache, planeMask, clipPlanes)
+ {
+ if (singlePath && (this._parentNodes.length > 1))
+ singlePath = false;
+
+ if (singlePath && (invalidateCache = invalidateCache || this.cacheInvalid()))
+ this.invalidateCache();
+
+ planeMask = drawableCollection.cull(transform, this.graphState(), singlePath, planeMask);
+ if (planeMask <= 0) {
+ return;
+ }
+
+ var viewarea = this._nameSpace.doc._viewarea;
+ var isMoving = viewarea.isMovingOrAnimating();
+
+ var ts = new Date().getTime();
+ var maxLiveTime = 10000;
+ var i, n, numChild = this._childNodes.length;
+
+ if (!this._vf.enableCulling)
+ {
+ n = this.getNumRenderedObjects(numChild, isMoving);
+
+ var cnt = 0;
+ for (i=0; i<numChild; i++)
+ {
+ var shape = this._childNodes[i];
+
+ if (shape)
+ {
+ var needCleanup = true;
+
+ if (this._visibleList[i] && cnt < n &&
+ shape.collectDrawableObjects(transform, drawableCollection, singlePath, invalidateCache, planeMask, clipPlanes))
+ {
+ this._createTime[i] = ts;
+ cnt++;
+ needCleanup = false;
+ }
+
+ if (needCleanup && !isMoving && this._createTime[i] > 0 &&
+ ts - this._createTime[i] > maxLiveTime && shape._cleanupGLObjects)
+ {
+ shape._cleanupGLObjects(true);
+ this._createTime[i] = 0;
+ }
+ }
+ }
+
+ return;
+ }
+
+ if (this._websocket)
+ this._websocket.updateCamera();
+
+ if (this._vf.label.length)
+ {
+ n = this.getNumRenderedObjects(this._idList.length, isMoving);
+
+ for (i=0; i<n; i++)
+ {
+ var obj = this._nameObjMap[this._idList[i]];
+ if (obj && obj.shape) {
+ obj.shape.collectDrawableObjects(transform, drawableCollection, singlePath, invalidateCache, planeMask, clipPlanes);
+ this._createTime[obj.pos] = ts;
+ }
+ else
+ x3dom.debug.logError("Invalid label: " + this._idList[i]);
+ }
+
+ for (i=0; i<this._childNodes.length; i++)
+ {
+ if (this._childNodes[i] && !isMoving && this._createTime[i] > 0 &&
+ ts - this._createTime[i] > maxLiveTime && this._childNodes[i]._cleanupGLObjects)
+ {
+ this._childNodes[i]._cleanupGLObjects(true);
+ this._createTime[i] = 0;
+ }
+ }
+ }
+ }
+ }
+ )
+);
+/** @namespace x3dom.nodeTypes */
+/*
+ * X3DOM JavaScript Library
+ * http://www.x3dom.org
+ *
+ * (C)2009 Fraunhofer IGD, Darmstadt, Germany
+ * Dual licensed under the MIT and GPL
+ */
+
+// Not a real X3D node type
+// ### Scene ###
+x3dom.registerNodeType(
+ "Scene",
+ "Grouping",
+ defineClass(x3dom.nodeTypes.X3DGroupingNode,
+
+ /**
+ * Constructor for Scene
+ * @constructs x3dom.nodeTypes.Scene
+ * @x3d x.x
+ * @component Core
+ * @extends x3dom.nodeTypes.X3DGroupingNode
+ * @param {Object} [ctx=null] - context object, containing initial settings like namespace
+ * @classdesc The scene node wraps the x3d scene.
+ */
+ function (ctx) {
+ x3dom.nodeTypes.Scene.superClass.call(this, ctx);
+
+ // define the experimental picking mode: box, idBuf, idBuf24, idBufId, color, texCoord
+
+ /**
+ * The picking mode for the scene
+ * @var {x3dom.fields.SFString} pickMode
+ * @memberof x3dom.nodeTypes.Scene
+ * @initvalue "idBuf"
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFString(ctx, 'pickMode', "idBuf");
+ // experimental field to switch off picking
+
+ /**
+ * Flag to enable/disable pick pass
+ * @var {x3dom.fields.SFBool} doPickPass
+ * @memberof x3dom.nodeTypes.Scene
+ * @initvalue true
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFBool(ctx, 'doPickPass', true);
+
+ // another experimental field for shadow DOM remapping
+
+ /**
+ * The url of the shadow object id mapping
+ * @var {x3dom.fields.SFString} shadowObjectIdMapping
+ * @memberof x3dom.nodeTypes.Scene
+ * @initvalue ""
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFString(ctx, 'shadowObjectIdMapping', "");
+
+ this._lastMin = new x3dom.fields.SFVec3f(0, 0, 0);
+ this._lastMax = new x3dom.fields.SFVec3f(1, 1, 1);
+
+ this._shadowIdMap = null;
+ this.loadMapping();
+
+ this._multiPartMap = null;
+ },
+ {
+ /* Bindable getter (e.g. getViewpoint) are added automatically */
+
+ fieldChanged: function(fieldName)
+ {
+ if (fieldName == "shadowObjectIdMapping")
+ {
+ this.loadMapping();
+ }
+ },
+
+ updateVolume: function()
+ {
+ var vol = this.getVolume();
+
+ if (vol.isValid())
+ {
+ this._lastMin = x3dom.fields.SFVec3f.copy(vol.min);
+ this._lastMax = x3dom.fields.SFVec3f.copy(vol.max);
+ }
+ },
+
+ loadMapping: function()
+ {
+ this._shadowIdMap = null;
+
+ if (this._vf.shadowObjectIdMapping.length == 0) {
+ return;
+ }
+
+ var that = this;
+ var xhr = new XMLHttpRequest();
+
+ xhr.open("GET", this._nameSpace.getURL(this._vf.shadowObjectIdMapping), true);
+ xhr.send();
+
+ xhr.onload = function()
+ {
+ that._shadowIdMap = eval("(" + xhr.response + ")");
+
+ if (!that._shadowIdMap || !that._shadowIdMap.mapping) {
+ x3dom.debug.logWarning("Invalid ID map: " + that._vf.shadowObjectIdMapping);
+ }
+ else {
+ x3dom.debug.assert(that._shadowIdMap.maxID <= that._shadowIdMap.mapping.length,
+ "Too few ID map entries in " + that._vf.shadowObjectIdMapping + ", " +
+ "length of mapping array is only " + that._shadowIdMap.mapping.length +
+ " instead of " + that._shadowIdMap.ids.length + "!");
+ }
+ };
+ }
+ }
+ )
+);
+
+/*
+ * X3DOM JavaScript Library
+ * http://www.x3dom.org
+ *
+ * (C)2009 Fraunhofer IGD, Darmstadt, Germany
+ * Dual licensed under the MIT and GPL
+ *
+ * Based on code originally provided by
+ * Philip Taylor: http://philip.html5.org
+ */
+
+
+///////////////////////////////////////////////////////////////////////////////
+// BindableStack constructor
+///////////////////////////////////////////////////////////////////////////////
+x3dom.BindableStack = function (doc, type, defaultType, getter) {
+ this._doc = doc;
+ this._type = type;
+ this._defaultType = defaultType;
+ this._defaultRoot = null;
+ this._getter = getter;
+ this._bindBag = [];
+ this._bindStack = [];
+};
+
+x3dom.BindableStack.prototype.top = function () {
+ return ( (this._bindStack.length > 0) ? this._bindStack[this._bindStack.length - 1] : null );
+};
+
+x3dom.BindableStack.prototype.push = function (bindable) {
+ var top = this.top();
+
+ if (top === bindable) {
+ return;
+ }
+
+ if (top) {
+ top.deactivate();
+ }
+
+ this._bindStack.push(bindable);
+
+ bindable.activate(top);
+};
+
+x3dom.BindableStack.prototype.replaceTop = function (bindable) {
+ var top = this.top();
+
+ if (top === bindable) {
+ return;
+ }
+
+ if (top) {
+ top.deactivate();
+
+ this._bindStack[this._bindStack.length - 1] = bindable;
+
+ bindable.activate(top);
+ }
+};
+
+x3dom.BindableStack.prototype.pop = function (bindable) {
+ var top;
+
+ if (bindable) {
+ top = this.top();
+ if (bindable !== top) {
+ return null;
+ }
+ }
+
+ top = this._bindStack.pop();
+
+ if (top) {
+ top.deactivate();
+ }
+
+ return top;
+};
+
+x3dom.BindableStack.prototype.switchTo = function (target) {
+ var last = this.getActive();
+ var n = this._bindBag.length;
+ var toBind = 0;
+ var i = 0, lastIndex = -1;
+
+ if (n <= 1) {
+ return;
+ }
+
+ switch (target)
+ {
+ case 'first':
+ toBind = this._bindBag[0];
+ break;
+ case 'last':
+ toBind = this._bindBag[n-1];
+ break;
+ default:
+ for (i = 0; i < n; i++) {
+ if (this._bindBag[i] == last) {
+ lastIndex = i;
+ break;
+ }
+ }
+ if (lastIndex >= 0) {
+ i = lastIndex;
+ while (!toBind) {
+ if (target == 'next') {
+ i = (i < (n-1)) ? (i+1) : 0;
+ } else { // prev
+ i = (i>0) ? (i-1) : (n-1);
+ }
+ if (i == lastIndex) {
+ break;
+ }
+ if (this._bindBag[i]._vf.description.length >= 0) {
+ toBind = this._bindBag[i];
+ }
+ }
+ }
+ break;
+ }
+
+ if (toBind) {
+ this.replaceTop(toBind);
+ } else {
+ x3dom.debug.logWarning ('Cannot switch bindable; no other bindable with description found.');
+ }
+};
+
+// Get currently active bindable of given stack type, creates new if none exists
+x3dom.BindableStack.prototype.getActive = function () {
+ if (this._bindStack.length === 0) {
+ if (this._bindBag.length === 0) {
+ if (this._defaultRoot) {
+ x3dom.debug.logInfo ('create new ' + this._defaultType._typeName +
+ ' for ' + this._type._typeName + '-stack');
+ var obj = new this._defaultType(
+ { doc: this._doc, nameSpace: this._defaultRoot._nameSpace, autoGen: true } );
+
+ this._defaultRoot.addChild(obj);
+ obj.nodeChanged();
+ }
+ else {
+ x3dom.debug.logError ('stack without defaultRoot');
+ }
+ }
+ else {
+ x3dom.debug.logInfo ('activate first ' + this._type._typeName +
+ ' for ' + this._type._typeName + '-stack');
+ }
+
+ this._bindStack.push(this._bindBag[0]);
+ this._bindBag[0].activate();
+ }
+
+ return this._bindStack[this._bindStack.length - 1];
+};
+
+
+///////////////////////////////////////////////////////////////////////////////
+// BindableBag constructor
+///////////////////////////////////////////////////////////////////////////////
+x3dom.BindableBag = function (doc) {
+ this._stacks = [];
+
+ this.addType ("X3DViewpointNode", "Viewpoint", "getViewpoint", doc);
+ this.addType ("X3DNavigationInfoNode", "NavigationInfo", "getNavigationInfo", doc);
+ this.addType ("X3DBackgroundNode", "Background", "getBackground", doc);
+ this.addType ("X3DFogNode", "Fog", "getFog", doc);
+ this.addType ("X3DEnvironmentNode", "Environment", "getEnvironment", doc);
+};
+
+x3dom.BindableBag.prototype.addType = function(typeName, defaultTypeName, getter, doc) {
+ var type = x3dom.nodeTypes[typeName];
+ var defaultType = x3dom.nodeTypes[defaultTypeName];
+
+ if (type && defaultType) {
+ var stack = new x3dom.BindableStack (doc, type, defaultType, getter);
+ this._stacks.push(stack);
+ }
+ else {
+ x3dom.debug.logWarning('Invalid Bindable type/defaultType: ' +
+ typeName + '/' + defaultType);
+ }
+};
+
+x3dom.BindableBag.prototype.setRefNode = function (node) {
+ Array.forEach ( this._stacks, function (stack) {
+ // set reference to Scene
+ stack._defaultRoot = node;
+ node[stack._getter] = function () { return stack.getActive(); };
+ } );
+};
+
+x3dom.BindableBag.prototype.addBindable = function(node) {
+ for (var i = 0, n = this._stacks.length; i < n; i++) {
+ var stack = this._stacks[i];
+
+ if ( x3dom.isa (node, stack._type) ) {
+ x3dom.debug.logInfo('register ' + node.typeName() + 'Bindable ' +
+ node._DEF + '/' + node._vf.description);
+
+ stack._bindBag.push(node);
+
+ var top = stack.top();
+
+ if (top && top._autoGen) {
+ stack.replaceTop(node);
+
+ // remove auto-generated default bindable
+ for (var j = 0, m = stack._bindBag.length; j < m; j++) {
+ if (stack._bindBag[j] === top) {
+ stack._bindBag.splice(j, 1);
+ break;
+ }
+ }
+ stack._defaultRoot.removeChild(top);
+ }
+
+ return stack;
+ }
+ }
+
+ x3dom.debug.logError (node.typeName() + ' is not a valid bindable');
+ return null;
+};
+
+/** @namespace x3dom.nodeTypes */
+/*
+ * X3DOM JavaScript Library
+ * http://www.x3dom.org
+ *
+ * (C)2009 Fraunhofer IGD, Darmstadt, Germany
+ * Dual licensed under the MIT and GPL
+ */
+
+/* ### X3DGeometryNode ### */
+x3dom.registerNodeType(
+ "X3DGeometryNode",
+ "Rendering",
+ defineClass(x3dom.nodeTypes.X3DNode,
+
+ /**
+ * Constructor for X3DGeometryNode
+ * @constructs x3dom.nodeTypes.X3DGeometryNode
+ * @x3d 3.3
+ * @component Rendering
+ * @status full
+ * @extends x3dom.nodeTypes.X3DNode
+ * @param {Object} [ctx=null] - context object, containing initial settings like namespace
+ * @classdesc This is the base node type for all geometry in X3D.
+ */
+ function (ctx) {
+ x3dom.nodeTypes.X3DGeometryNode.superClass.call(this, ctx);
+
+
+ /**
+ * Specifies whether backface-culling is used. If solid is TRUE only front-faces are drawn.
+ * @var {x3dom.fields.SFBool} solid
+ * @memberof x3dom.nodeTypes.X3DGeometryNode
+ * @initvalue true
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFBool(ctx, 'solid', true);
+
+ /**
+ * The ccw field defines the ordering of the vertex coordinates of the geometry with respect to user-given or automatically generated normal vectors used in the lighting model equations.
+ * @var {x3dom.fields.SFBool} ccw
+ * @memberof x3dom.nodeTypes.X3DGeometryNode
+ * @initvalue true
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFBool(ctx, 'ccw', true);
+
+ /**
+ * Most geo primitives use geo cache and others might later on, but one should be able to disable cache per geometry node.
+ * @var {x3dom.fields.SFBool} useGeoCache
+ * @memberof x3dom.nodeTypes.X3DGeometryNode
+ * @initvalue true
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFBool(ctx, 'useGeoCache', true);
+
+ /**
+ * Specifies whether this geometry should be rendered with or without lighting.
+ * @var {x3dom.fields.SFBool} lit
+ * @memberof x3dom.nodeTypes.X3DGeometryNode
+ * @initvalue true
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFBool(ctx, 'lit', true);
+
+ // mesh object also holds volume (_vol)
+ this._mesh = new x3dom.Mesh(this);
+
+ },
+ {
+ getVolume: function() {
+ // geometry doesn't hold volume, but mesh does
+ return this._mesh.getVolume();
+ },
+
+ invalidateVolume: function() {
+ this._mesh.invalidate();
+ },
+
+ getCenter: function() {
+ return this._mesh.getCenter();
+ },
+
+ getDiameter: function() {
+ return this._mesh.getDiameter();
+ },
+
+ doIntersect: function(line) {
+ return this._mesh.doIntersect(line);
+ },
+
+ forceUpdateCoverage: function() {
+ return false;
+ },
+
+ hasIndexOffset: function() {
+ return false;
+ },
+
+ getColorTexture: function() {
+ return null;
+ },
+
+ getColorTextureURL: function() {
+ return null;
+ },
+
+ parentAdded: function(parent) {
+ if (x3dom.isa(parent, x3dom.nodeTypes.X3DShapeNode)) {
+ if (parent._cleanupGLObjects) {
+ parent._cleanupGLObjects(true);
+ }
+ parent.setAllDirty();
+ parent.invalidateVolume();
+ }
+ },
+
+ needLighting: function() {
+ var hasTris = this._mesh._primType.indexOf("TRIANGLE") >= 0;
+ return (this._vf.lit && hasTris);
+ }
+ }
+ )
+);
+/** @namespace x3dom.nodeTypes */
+/*
+ * X3DOM JavaScript Library
+ * http://www.x3dom.org
+ *
+ * (C)2009 Fraunhofer IGD, Darmstadt, Germany
+ * Dual licensed under the MIT and GPL
+ */
+
+/* ### Mesh ### */
+x3dom.registerNodeType(
+ "Mesh", // experimental WebSG geo node
+ "Rendering",
+ defineClass(x3dom.nodeTypes.X3DGeometryNode,
+
+ /**
+ * Constructor for Mesh
+ * @constructs x3dom.nodeTypes.Mesh
+ * @x3d x.x
+ * @component Rendering
+ * @extends x3dom.nodeTypes.X3DGeometryNode
+ * @param {Object} [ctx=null] - context object, containing initial settings like namespace
+ * @classdesc This is an experimental WebSG geo node
+ */
+ function (ctx) {
+ x3dom.nodeTypes.Mesh.superClass.call(this, ctx);
+
+
+ /**
+ *
+ * @var {x3dom.fields.SFString} primType
+ * @memberof x3dom.nodeTypes.Mesh
+ * @initvalue "triangle"
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFString(ctx, 'primType', "triangle");
+
+ /**
+ *
+ * @var {x3dom.fields.MFInt32} index
+ * @memberof x3dom.nodeTypes.Mesh
+ * @initvalue []
+ * @field x3dom
+ * @instance
+ */
+ this.addField_MFInt32(ctx, 'index', []);
+
+
+ /**
+ *
+ * @var {x3dom.fields.MFNode} vertexAttributes
+ * @memberof x3dom.nodeTypes.Mesh
+ * @initvalue x3dom.nodeTypes.X3DVertexAttributeNode
+ * @field x3dom
+ * @instance
+ */
+ this.addField_MFNode('vertexAttributes', x3dom.nodeTypes.X3DVertexAttributeNode);
+
+ },
+ {
+ nodeChanged: function()
+ {
+ var time0 = new Date().getTime();
+
+ var i, n = this._cf.vertexAttributes.nodes.length;
+
+ for (i=0; i<n; i++)
+ {
+ var name = this._cf.vertexAttributes.nodes[i]._vf.name;
+
+ switch (name.toLowerCase())
+ {
+ case "position":
+ this._mesh._positions[0] = this._cf.vertexAttributes.nodes[i]._vf.value.toGL();
+ break;
+ case "normal":
+ this._mesh._normals[0] = this._cf.vertexAttributes.nodes[i]._vf.value.toGL();
+ break;
+ case "texcoord":
+ this._mesh._texCoords[0] = this._cf.vertexAttributes.nodes[i]._vf.value.toGL();
+ break;
+ case "color":
+ this._mesh._colors[0] = this._cf.vertexAttributes.nodes[i]._vf.value.toGL();
+ break;
+ default:
+ this._mesh._dynamicFields[name] = {};
+ this._mesh._dynamicFields[name].numComponents =
+ this._cf.vertexAttributes.nodes[i]._vf.numComponents;
+ this._mesh._dynamicFields[name].value =
+ this._cf.vertexAttributes.nodes[i]._vf.value.toGL();
+ break;
+ }
+ }
+
+ this._mesh._indices[0] = this._vf.index.toGL();
+
+ this.invalidateVolume();
+
+ this._mesh._numFaces = this._mesh._indices[0].length / 3;
+ this._mesh._numCoords = this._mesh._positions[0].length / 3;
+
+ var time1 = new Date().getTime() - time0;
+ x3dom.debug.logWarning("Mesh load time: " + time1 + " ms");
+ }
+ }
+ )
+);
+/** @namespace x3dom.nodeTypes */
+/*
+ * X3DOM JavaScript Library
+ * http://www.x3dom.org
+ *
+ * (C)2009 Fraunhofer IGD, Darmstadt, Germany
+ * Dual licensed under the MIT and GPL
+ */
+
+/* ### PointSet ### */
+x3dom.registerNodeType(
+ "PointSet",
+ "Rendering",
+ defineClass(x3dom.nodeTypes.X3DGeometryNode,
+
+ /**
+ * Constructor for PointSet
+ * @constructs x3dom.nodeTypes.PointSet
+ * @x3d 3.3
+ * @component Rendering
+ * @status experimental
+ * @extends x3dom.nodeTypes.X3DGeometryNode
+ * @param {Object} [ctx=null] - context object, containing initial settings like namespace
+ * @classdesc PointSet is a node that contains a set of colored 3D points, represented by contained Color and Coordinate nodes.
+ * Color values or a Material emissiveColor is used to draw lines and points. Hint: use a different color (or emissiveColor) than the background color.
+ * Hint: insert a Shape node before adding geometry or Appearance. You can also substitute a type-matched ProtoInstance for content.
+ */
+ function (ctx) {
+ x3dom.nodeTypes.PointSet.superClass.call(this, ctx);
+
+
+ /**
+ * Coordinate node specifiying the vertices used by the geometry.
+ * @var {x3dom.fields.SFNode} coord
+ * @memberof x3dom.nodeTypes.PointSet
+ * @initvalue x3dom.nodeTypes.X3DCoordinateNode
+ * @field x3d
+ * @instance
+ */
+ this.addField_SFNode('coord', x3dom.nodeTypes.X3DCoordinateNode);
+
+ /**
+ * If NULL the geometry is rendered using the Material and texture defined in the Appearance node.
+ * If not NULL the field shall contain a Color node whose colours are applied depending on the value of "colorPerVertex".
+ * @var {x3dom.fields.SFNode} color
+ * @memberof x3dom.nodeTypes.PointSet
+ * @initvalue x3dom.nodeTypes.X3DColorNode
+ * @field x3d
+ * @instance
+ */
+ this.addField_SFNode('color', x3dom.nodeTypes.X3DColorNode);
+
+ this._mesh._primType = 'POINTS';
+
+ },
+ {
+ nodeChanged: function()
+ {
+ var time0 = new Date().getTime();
+
+ var coordNode = this._cf.coord.node;
+ x3dom.debug.assert(coordNode, "PointSet without coord node!");
+ var positions = coordNode.getPoints();
+
+ var numColComponents = 3;
+ var colorNode = this._cf.color.node;
+ var colors = new x3dom.fields.MFColor();
+ if (colorNode) {
+ colors = colorNode._vf.color;
+ x3dom.debug.assert(positions.length == colors.length, "Size of color and coord array differs!");
+
+ if (x3dom.isa(colorNode, x3dom.nodeTypes.ColorRGBA)) {
+ numColComponents = 4;
+ }
+ }
+
+ this._mesh._numColComponents = numColComponents;
+ this._mesh._lit = false;
+
+ this._mesh._indices[0] = [];
+ this._mesh._positions[0] = positions.toGL();
+ this._mesh._colors[0] = colors.toGL();
+ this._mesh._normals[0] = [];
+ this._mesh._texCoords[0] = [];
+
+ this.invalidateVolume();
+ this._mesh._numCoords = this._mesh._positions[0].length / 3;
+
+ var time1 = new Date().getTime() - time0;
+ //x3dom.debug.logInfo("Mesh load time: " + time1 + " ms");
+ },
+
+ fieldChanged: function(fieldName)
+ {
+ var pnts = null;
+
+ if (fieldName == "coord")
+ {
+ pnts = this._cf.coord.node.getPoints();
+
+ this._mesh._positions[0] = pnts.toGL();
+
+ this.invalidateVolume();
+
+ Array.forEach(this._parentNodes, function (node) {
+ node._dirty.positions = true;
+ node.invalidateVolume();
+ });
+ }
+ else if (fieldName == "color")
+ {
+ pnts = this._cf.color.node._vf.color;
+
+ this._mesh._colors[0] = pnts.toGL();
+
+ Array.forEach(this._parentNodes, function (node) {
+ node._dirty.colors = true;
+ });
+ }
+ }
+ }
+ )
+);
+/** @namespace x3dom.nodeTypes */
+/*
+ * X3DOM JavaScript Library
+ * http://www.x3dom.org
+ *
+ * (C)2009 Fraunhofer IGD, Darmstadt, Germany
+ * Dual licensed under the MIT and GPL
+ */
+
+/* ### X3DComposedGeometryNode ### */
+x3dom.registerNodeType(
+ "X3DComposedGeometryNode",
+ "Rendering",
+ defineClass(x3dom.nodeTypes.X3DGeometryNode,
+
+ /**
+ * Constructor for X3DComposedGeometryNode
+ * @constructs x3dom.nodeTypes.X3DComposedGeometryNode
+ * @x3d 3.3
+ * @component Rendering
+ * @status experimental
+ * @extends x3dom.nodeTypes.X3DGeometryNode
+ * @param {Object} [ctx=null] - context object, containing initial settings like namespace
+ * @classdesc This is the base node type for all composed 3D geometry in X3D.
+ * A composed geometry node type defines an abstract type that composes geometry from a set of nodes that define individual components.
+ * Composed geometry may have color, coordinates, normal and texture coordinates supplied.
+ * The rendered output of the combination of these is dependent on the concrete node definition.
+ */
+ function (ctx) {
+ x3dom.nodeTypes.X3DComposedGeometryNode.superClass.call(this, ctx);
+
+
+ /**
+ * If colorPerVertex is FALSE, colours are applied to each face. If colorPerVertex is true, colours are applied to each vertex.
+ * @var {x3dom.fields.SFBool} colorPerVertex
+ * @memberof x3dom.nodeTypes.X3DComposedGeometryNode
+ * @initvalue true
+ * @field x3d
+ * @instance
+ */
+ this.addField_SFBool(ctx, 'colorPerVertex', true);
+
+ /**
+ * If normalPerVertex is FALSE, colours are applied to each face. If normalPerVertex is true, normals are applied to each vertex.
+ * @var {x3dom.fields.SFBool} normalPerVertex
+ * @memberof x3dom.nodeTypes.X3DComposedGeometryNode
+ * @initvalue true
+ * @field x3d
+ * @instance
+ */
+ this.addField_SFBool(ctx, 'normalPerVertex', true);
+
+ /**
+ *
+ * @var {x3dom.fields.SFString} normalUpdateMode
+ * @memberof x3dom.nodeTypes.X3DComposedGeometryNode
+ * @initvalue 'fast'
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFString(ctx, 'normalUpdateMode', 'fast'); // none; fast; nice
+
+
+ /**
+ * If the attrib field is not empty it shall contain a list of per-vertex attribute information for programmable shaders.
+ * @var {x3dom.fields.MFNode} attrib
+ * @memberof x3dom.nodeTypes.X3DComposedGeometryNode
+ * @initvalue x3dom.nodeTypes.X3DVertexAttributeNode
+ * @field x3dom
+ * @instance
+ */
+ this.addField_MFNode('attrib', x3dom.nodeTypes.X3DVertexAttributeNode);
+
+
+ /**
+ * Contains a Coordinate node.
+ * @var {x3dom.fields.SFNode} coord
+ * @memberof x3dom.nodeTypes.X3DComposedGeometryNode
+ * @initvalue x3dom.nodeTypes.X3DCoordinateNode
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFNode('coord', x3dom.nodeTypes.X3DCoordinateNode);
+
+ /**
+ * If the normal field is not NULL, it shall contain a Normal node whose normals are applied to the vertices or faces of the X3DComposedGeometryNode in a manner exactly equivalent to that described above for applying colours to vertices/faces (where normalPerVertex corresponds to colorPerVertex and normalIndex corresponds to colorIndex).
+ * If the normal field is NULL, the browser shall automatically generate normals in accordance with the node's definition. If the node does not define a behaviour, the default is to generate an averaged normal for all faces that share that vertex.
+ * @var {x3dom.fields.SFNode} normal
+ * @memberof x3dom.nodeTypes.X3DComposedGeometryNode
+ * @initvalue x3dom.nodeTypes.Normal
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFNode('normal', x3dom.nodeTypes.Normal);
+
+ /**
+ * If the color field is NULL, the geometry shall be rendered normally using the Material and texture defined in the Appearance node.
+ * @var {x3dom.fields.SFNode} color
+ * @memberof x3dom.nodeTypes.X3DComposedGeometryNode
+ * @initvalue x3dom.nodeTypes.X3DColorNode
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFNode('color', x3dom.nodeTypes.X3DColorNode);
+
+ /**
+ * If the texCoord field is not NULL, it shall contain a TextureCoordinate node.
+ * @var {x3dom.fields.SFNode} texCoord
+ * @memberof x3dom.nodeTypes.X3DComposedGeometryNode
+ * @initvalue x3dom.nodeTypes.X3DTextureCoordinateNode
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFNode('texCoord', x3dom.nodeTypes.X3DTextureCoordinateNode);
+
+ },
+ {
+ handleAttribs: function()
+ {
+ //var time0 = new Date().getTime();
+
+ // TODO; handle case that more than 2^16-1 attributes are to be referenced
+ var i, n = this._cf.attrib.nodes.length;
+
+ for (i=0; i<n; i++)
+ {
+ var name = this._cf.attrib.nodes[i]._vf.name;
+
+ switch (name.toLowerCase())
+ {
+ case "position":
+ this._mesh._positions[0] = this._cf.attrib.nodes[i]._vf.value.toGL();
+ break;
+ case "normal":
+ this._mesh._normals[0] = this._cf.attrib.nodes[i]._vf.value.toGL();
+ break;
+ case "texcoord":
+ this._mesh._texCoords[0] = this._cf.attrib.nodes[i]._vf.value.toGL();
+ break;
+ case "color":
+ this._mesh._colors[0] = this._cf.attrib.nodes[i]._vf.value.toGL();
+ break;
+ default:
+ this._mesh._dynamicFields[name] = {};
+ this._mesh._dynamicFields[name].numComponents =
+ this._cf.attrib.nodes[i]._vf.numComponents;
+ this._mesh._dynamicFields[name].value =
+ this._cf.attrib.nodes[i]._vf.value.toGL();
+ break;
+ }
+ }
+
+ //var time1 = new Date().getTime() - time0;
+ //x3dom.debug.logInfo("Mesh load time: " + time1 + " ms");
+ }
+ }
+ )
+);
+/** @namespace x3dom.nodeTypes */
+/*
+ * X3DOM JavaScript Library
+ * http://www.x3dom.org
+ *
+ * (C)2009 Fraunhofer IGD, Darmstadt, Germany
+ * Dual licensed under the MIT and GPL
+ */
+
+/* ### LineSet ### */
+x3dom.registerNodeType(
+ "LineSet",
+ "Rendering",
+ defineClass(x3dom.nodeTypes.X3DGeometryNode,
+
+ /**
+ * Constructor for LineSet
+ * @constructs x3dom.nodeTypes.LineSet
+ * @x3d 3.3
+ * @component Rendering
+ * @status experimental
+ * @extends x3dom.nodeTypes.X3DGeometryNode
+ * @param {Object} [ctx=null] - context object, containing initial settings like namespace
+ * @classdesc LineSet is a geometry node that can contain a Color node and a Coordinate node.
+ * Color values or a Material emissiveColor is used to draw lines and points.
+ * Lines are not lit, are not texture-mapped, and do not participate in collision detection.
+ * Hint: use a different color (or emissiveColor) than the background color.
+ * Hint: if rendering Coordinate points originally defined for an IndexedFaceSet, index values may need to repeat each initial vertex to close each polygon outline.
+ * Hint: insert a Shape node before adding geometry or Appearance.
+ */
+ function (ctx) {
+ x3dom.nodeTypes.LineSet.superClass.call(this, ctx);
+
+
+ /**
+ * vertexCount describes how many vertices are used in each polyline from Coordinate field. Coordinates are assigned to each line by taking vertexCount[n] vertices from Coordinate field.
+ * @var {x3dom.fields.MFInt32} vertexCount
+ * @range [2, inf]
+ * @memberof x3dom.nodeTypes.LineSet
+ * @initvalue []
+ * @field x3d
+ * @instance
+ */
+ this.addField_MFInt32(ctx, 'vertexCount', []);
+
+
+ /**
+ * If the "attrib" field is not empty it shall contain a list of per-vertex attribute information for programmable shaders
+ * @var {x3dom.fields.MFNode} attrib
+ * @memberof x3dom.nodeTypes.LineSet
+ * @initvalue x3dom.nodeTypes.X3DVertexAttributeNode
+ * @field x3d
+ * @instance
+ */
+ this.addField_MFNode('attrib', x3dom.nodeTypes.X3DVertexAttributeNode);
+
+ /**
+ * Coordinate node specifiying the vertices used by the geometry.
+ * @var {x3dom.fields.SFNode} coord
+ * @memberof x3dom.nodeTypes.LineSet
+ * @initvalue x3dom.nodeTypes.X3DCoordinateNode
+ * @field x3d
+ * @instance
+ */
+ this.addField_SFNode('coord', x3dom.nodeTypes.X3DCoordinateNode);
+
+ /**
+ * If NULL the geometry is rendered using the Material and texture defined in the Appearance node. If not NULL the field shall contain a Color node whose colours are applied depending on the value of "colorPerVertex".
+ * @var {x3dom.fields.SFNode} color
+ * @memberof x3dom.nodeTypes.LineSet
+ * @initvalue x3dom.nodeTypes.X3DColorNode
+ * @field x3d
+ * @instance
+ */
+ this.addField_SFNode('color', x3dom.nodeTypes.X3DColorNode);
+
+ this._mesh._primType = "LINES";
+ x3dom.Utils.needLineWidth = true;
+
+ },
+ {
+ nodeChanged: function() {
+ var coordNode = this._cf.coord.node;
+ x3dom.debug.assert(coordNode);
+ var positions = coordNode.getPoints();
+
+ this._mesh._positions[0] = positions.toGL();
+
+ var colorNode = this._cf.color.node;
+ if (colorNode) {
+ var colors = colorNode._vf.color;
+
+ this._mesh._colors[0] = colors.toGL();
+
+ this._mesh._numColComponents = 3;
+ if (x3dom.isa(colorNode, x3dom.nodeTypes.ColorRGBA)) {
+ this._mesh._numColComponents = 4;
+ }
+ }
+
+ var cnt = 0;
+ this._mesh._indices[0] = [];
+
+ for (var i=0, n=this._vf.vertexCount.length; i<n; i++) {
+ var vc = this._vf.vertexCount[i];
+ if (vc < 2) {
+ x3dom.debug.logError("LineSet.vertexCount must not be smaller than 2!");
+ break;
+ }
+ for (var j=vc-2; j>=0; j--) {
+ this._mesh._indices[0].push(cnt++, cnt);
+ if (j == 0) cnt++;
+ }
+ }
+ },
+
+ fieldChanged: function(fieldName) {
+ if (fieldName == "coord") {
+ var pnts = this._cf.coord.node.getPoints();
+ this._mesh._positions[0] = pnts.toGL();
+
+ this.invalidateVolume();
+ Array.forEach(this._parentNodes, function (node) {
+ node._dirty.positions = true;
+ node.invalidateVolume();
+ });
+ }
+ else if (fieldName == "color") {
+ var cols = this._cf.color.node._vf.color;
+ this._mesh._colors[0] = cols.toGL();
+
+ Array.forEach(this._parentNodes, function (node) {
+ node._dirty.colors = true;
+ });
+ }
+ }
+ }
+ )
+);
+/** @namespace x3dom.nodeTypes */
+/*
+ * X3DOM JavaScript Library
+ * http://www.x3dom.org
+ *
+ * (C)2009 Fraunhofer IGD, Darmstadt, Germany
+ * Dual licensed under the MIT and GPL
+ */
+
+/* ### IndexedLineSet ### */
+x3dom.registerNodeType(
+ "IndexedLineSet",
+ "Rendering",
+ defineClass(x3dom.nodeTypes.X3DGeometryNode,
+
+ /**
+ * Constructor for IndexedLineSet
+ * @constructs x3dom.nodeTypes.IndexedLineSet
+ * @x3d 3.3
+ * @component Rendering
+ * @status experimental
+ * @extends x3dom.nodeTypes.X3DGeometryNode
+ * @param {Object} [ctx=null] - context object, containing initial settings like namespace
+ * @classdesc IndexedLineSet is a geometry node that can contain a Color node and a Coordinate node.
+ * Color values or a Material emissiveColor is used to draw lines and points. Lines are not lit, are not texture-mapped, and do not participate in collision detection.
+ * Hint: use a different color (or emissiveColor) than the background color.
+ * Hint: if rendering Coordinate points originally defined for an IndexedFaceSet, index values may need to repeat each initial vertex to close each polygon outline.
+ * Hint: insert a Shape node before adding geometry or Appearance. You can also substitute a type-matched ProtoInstance for content.
+ */
+ function (ctx) {
+ x3dom.nodeTypes.IndexedLineSet.superClass.call(this, ctx);
+
+
+ /**
+ * Whether Color node is applied per vertex (true) or per polygon (false).
+ * @var {x3dom.fields.SFBool} colorPerVertex
+ * @memberof x3dom.nodeTypes.IndexedLineSet
+ * @initvalue true
+ * @field x3d
+ * @instance
+ */
+ this.addField_SFBool(ctx, 'colorPerVertex', true); // TODO
+
+
+ /**
+ * If the "attrib" field is not empty it shall contain a list of per-vertex attribute information for programmable shaders
+ * @var {x3dom.fields.MFNode} attrib
+ * @memberof x3dom.nodeTypes.IndexedLineSet
+ * @initvalue x3dom.nodeTypes.X3DVertexAttributeNode
+ * @field x3d
+ * @instance
+ */
+ this.addField_MFNode('attrib', x3dom.nodeTypes.X3DVertexAttributeNode);
+
+ /**
+ * Coordinate node specifiying the vertices used by the geometry.
+ * @var {x3dom.fields.SFNode} coord
+ * @memberof x3dom.nodeTypes.IndexedLineSet
+ * @initvalue x3dom.nodeTypes.X3DCoordinateNode
+ * @field x3d
+ * @instance
+ */
+ this.addField_SFNode('coord', x3dom.nodeTypes.X3DCoordinateNode);
+
+ /**
+ * If NULL the geometry is rendered using the Material and texture defined in the Appearance node. If not NULL the field shall contain a Color node whose colours are applied depending on the value of "colorPerVertex".
+ * @var {x3dom.fields.SFNode} color
+ * @memberof x3dom.nodeTypes.IndexedLineSet
+ * @initvalue x3dom.nodeTypes.X3DColorNode
+ * @field x3d
+ * @instance
+ */
+ this.addField_SFNode('color', x3dom.nodeTypes.X3DColorNode);
+
+
+ /**
+ * coordIndex indices provide order in which coordinates are applied.
+ * Order starts at index 0, commas are optional between sets, use -1 to separate indices for each polyline.
+ * Hint: if rendering Coordinate points originally defined for an IndexedFaceSet, index values may need to repeat initial each initial vertex to close the polygons.
+ * @var {x3dom.fields.MFInt32} coordIndex
+ * @range [0, inf] or -1
+ * @memberof x3dom.nodeTypes.IndexedLineSet
+ * @initvalue []
+ * @field x3d
+ * @instance
+ */
+ this.addField_MFInt32(ctx, 'coordIndex', []);
+
+ /**
+ * colorIndex indices provide order in which colors are applied.
+ * Hint: if rendering Coordinate points originally defined for an IndexedFaceSet, index values may need to repeat initial each initial vertex to close the polygons.
+ * @var {x3dom.fields.MFInt32} colorIndex
+ * @range [0, inf] or -1
+ * @memberof x3dom.nodeTypes.IndexedLineSet
+ * @initvalue []
+ * @field x3d
+ * @instance
+ */
+ this.addField_MFInt32(ctx, 'colorIndex', []);
+
+ this._mesh._primType = 'LINES';
+ x3dom.Utils.needLineWidth = true;
+
+ },
+ {
+ nodeChanged: function()
+ {
+ var time0 = new Date().getTime();
+
+ // this.handleAttribs();
+
+ var indexes = this._vf.coordIndex;
+ var colorInd = this._vf.colorIndex;
+
+ var hasColor = false, hasColorInd = false;
+
+ // TODO; implement colorPerVertex also for single index
+ var colPerVert = this._vf.colorPerVertex;
+
+ if (colorInd.length > 0)
+ {
+ hasColorInd = true;
+ }
+
+ var positions, colors;
+
+ var coordNode = this._cf.coord.node;
+ x3dom.debug.assert(coordNode);
+
+ positions = coordNode.getPoints();
+
+ var numColComponents = 3;
+ var colorNode = this._cf.color.node;
+ if (colorNode)
+ {
+ hasColor = true;
+ colors = colorNode._vf.color;
+
+ if (x3dom.isa(colorNode, x3dom.nodeTypes.ColorRGBA)) {
+ numColComponents = 4;
+ }
+ }
+ else {
+ hasColor = false;
+ }
+
+ this._mesh._indices[0] = [];
+ this._mesh._positions[0] = [];
+ this._mesh._colors[0] = [];
+
+ var i, t, cnt, lineCnt;
+ var p0, p1, c0, c1;
+
+ // Found MultiIndex Mesh OR LineSet with too many vertices for 16 bit
+ if ( (hasColor && hasColorInd) || positions.length > x3dom.Utils.maxIndexableCoords )
+ {
+ t = 0;
+ cnt = 0;
+ lineCnt = 0;
+
+ for (i=0; i < indexes.length; ++i)
+ {
+ if (indexes[i] === -1) {
+ t = 0;
+ continue;
+ }
+
+ if (hasColorInd) {
+ x3dom.debug.assert(colorInd[i] != -1);
+ }
+
+ switch (t)
+ {
+ case 0:
+ p0 = +indexes[i];
+ if (hasColorInd && colPerVert) { c0 = +colorInd[i]; }
+ else { c0 = p0; }
+ t = 1;
+ break;
+ case 1:
+ p1 = +indexes[i];
+ if (hasColorInd && colPerVert) { c1 = +colorInd[i]; }
+ else if (hasColorInd && !colPerVert) { c1 = +colorInd[lineCnt]; }
+ else { c1 = p1; }
+
+ this._mesh._indices[0].push(cnt++, cnt++);
+
+ this._mesh._positions[0].push(positions[p0].x);
+ this._mesh._positions[0].push(positions[p0].y);
+ this._mesh._positions[0].push(positions[p0].z);
+ this._mesh._positions[0].push(positions[p1].x);
+ this._mesh._positions[0].push(positions[p1].y);
+ this._mesh._positions[0].push(positions[p1].z);
+
+ if (hasColor) {
+ if (!colPerVert) {
+ c0 = c1;
+ }
+ this._mesh._colors[0].push(colors[c0].r);
+ this._mesh._colors[0].push(colors[c0].g);
+ this._mesh._colors[0].push(colors[c0].b);
+ this._mesh._colors[0].push(colors[c1].r);
+ this._mesh._colors[0].push(colors[c1].g);
+ this._mesh._colors[0].push(colors[c1].b);
+ }
+
+ t = 2;
+ lineCnt++;
+ break;
+ case 2:
+ p0 = p1;
+ c0 = c1;
+ p1 = +indexes[i];
+ if (hasColorInd && colPerVert) { c1 = +colorInd[i]; }
+ else if (hasColorInd && !colPerVert) { c1 = +colorInd[lineCnt]; }
+ else { c1 = p1; }
+
+ this._mesh._indices[0].push(cnt++, cnt++);
+
+ this._mesh._positions[0].push(positions[p0].x);
+ this._mesh._positions[0].push(positions[p0].y);
+ this._mesh._positions[0].push(positions[p0].z);
+ this._mesh._positions[0].push(positions[p1].x);
+ this._mesh._positions[0].push(positions[p1].y);
+ this._mesh._positions[0].push(positions[p1].z);
+
+ if (hasColor) {
+ if (!colPerVert) {
+ c0 = c1;
+ }
+ this._mesh._colors[0].push(colors[c0].r);
+ this._mesh._colors[0].push(colors[c0].g);
+ this._mesh._colors[0].push(colors[c0].b);
+ this._mesh._colors[0].push(colors[c1].r);
+ this._mesh._colors[0].push(colors[c1].g);
+ this._mesh._colors[0].push(colors[c1].b);
+ }
+
+ lineCnt++;
+ break;
+ default:
+ }
+ }
+
+ //if the LineSet is too large for 16 bit indices, split it!
+ if (positions.length > x3dom.Utils.maxIndexableCoords)
+ this._mesh.splitMesh(2);
+ } // if isMulti
+ else
+ {
+ var n = indexes.length;
+ t = 0;
+
+ for (i=0; i < n; ++i)
+ {
+ if (indexes[i] == -1) {
+ t = 0;
+ continue;
+ }
+
+ switch (t) {
+ case 0: p0 = +indexes[i]; t = 1; break;
+ case 1: p1 = +indexes[i]; t = 2; this._mesh._indices[0].push(p0, p1); break;
+ case 2: p0 = p1; p1 = +indexes[i]; this._mesh._indices[0].push(p0, p1); break;
+ }
+ }
+
+ this._mesh._positions[0] = positions.toGL();
+
+ if (hasColor) {
+ this._mesh._colors[0] = colors.toGL();
+ this._mesh._numColComponents = numColComponents;
+ }
+ }
+
+ this.invalidateVolume();
+ this._mesh._numCoords = 0;
+
+ for (i=0; i<this._mesh._indices.length; i++) {
+ this._mesh._numCoords += this._mesh._positions[i].length / 3;
+ }
+
+ var time1 = new Date().getTime() - time0;
+ //x3dom.debug.logInfo("Mesh load time: " + time1 + " ms");
+ },
+
+ fieldChanged: function(fieldName)
+ {
+ var pnts = null;
+
+ if (fieldName == "coord")
+ {
+ pnts = this._cf.coord.node._vf.point;
+
+ this._mesh._positions[0] = pnts.toGL();
+
+ this.invalidateVolume();
+
+ Array.forEach(this._parentNodes, function (node) {
+ node._dirty.positions = true;
+ node.invalidateVolume();
+ });
+ }
+ else if (fieldName == "color")
+ {
+ pnts = this._cf.color.node._vf.color;
+
+ this._mesh._colors[0] = pnts.toGL();
+
+ Array.forEach(this._parentNodes, function (node) {
+ node._dirty.colors = true;
+ });
+ }
+ else if (fieldName == "coordIndex") {
+ this._mesh._indices[0] = [];
+
+ var indexes = this._vf.coordIndex;
+ var p0, p1, t = 0;
+
+ for (var i=0, n=indexes.length; i < n; ++i) {
+ if (indexes[i] == -1) {
+ t = 0;
+ }
+ else {
+ switch (t) {
+ case 0: p0 = +indexes[i]; t = 1; break;
+ case 1: p1 = +indexes[i]; t = 2; this._mesh._indices[0].push(p0, p1); break;
+ case 2: p0 = p1; p1 = +indexes[i]; this._mesh._indices[0].push(p0, p1); break;
+ }
+ }
+ }
+
+ this.invalidateVolume();
+
+ Array.forEach(this._parentNodes, function (node) {
+ node._dirty.indexes = true;
+ node.invalidateVolume();
+ });
+ }
+ }
+ }
+ )
+);
+/** @namespace x3dom.nodeTypes */
+/*
+ * X3DOM JavaScript Library
+ * http://www.x3dom.org
+ *
+ * (C)2009 Fraunhofer IGD, Darmstadt, Germany
+ * Dual licensed under the MIT and GPL
+ */
+
+/* ### IndexedTriangleSet ### */
+x3dom.registerNodeType(
+ "IndexedTriangleSet",
+ "Rendering",
+ defineClass(x3dom.nodeTypes.X3DComposedGeometryNode,
+
+ /**
+ * Constructor for IndexedTriangleSet
+ * @constructs x3dom.nodeTypes.IndexedTriangleSet
+ * @x3d 3.3
+ * @component Rendering
+ * @status experimental
+ * @extends x3dom.nodeTypes.X3DComposedGeometryNode
+ * @param {Object} [ctx=null] - context object, containing initial settings like namespace
+ * @classdesc IndexedTriangleSet is a geometry node that can contain a Color, Coordinate, Normal and TextureCoordinate node.
+ * Hint: insert a Shape node before adding geometry or Appearance.
+ * You can also substitute a type-matched ProtoInstance for content.
+ */
+ function (ctx) {
+ x3dom.nodeTypes.IndexedTriangleSet.superClass.call(this, ctx);
+
+
+ /**
+ * index specifies triangles by connecting Coordinate vertices.
+ * @var {x3dom.fields.MFInt32} index
+ * @range [0, inf]
+ * @memberof x3dom.nodeTypes.IndexedTriangleSet
+ * @initvalue []
+ * @field x3d
+ * @instance
+ */
+ this.addField_MFInt32(ctx, 'index', []);
+
+ },
+ {
+ nodeChanged: function()
+ {
+ var time0 = new Date().getTime();
+
+ this.handleAttribs();
+
+ var colPerVert = this._vf.colorPerVertex;
+ var normPerVert = this._vf.normalPerVertex;
+
+ var indexes = this._vf.index;
+
+ var hasNormal = false, hasTexCoord = false, hasColor = false;
+ var positions, normals, texCoords, colors;
+
+ var coordNode = this._cf.coord.node;
+ x3dom.debug.assert(coordNode);
+ positions = coordNode._vf.point;
+
+ var normalNode = this._cf.normal.node;
+ if (normalNode) {
+ hasNormal = true;
+ normals = normalNode._vf.vector;
+ }
+ else {
+ hasNormal = false;
+ }
+
+ var texMode = "", numTexComponents = 2;
+ var texCoordNode = this._cf.texCoord.node;
+ if (x3dom.isa(texCoordNode, x3dom.nodeTypes.MultiTextureCoordinate)) {
+ if (texCoordNode._cf.texCoord.nodes.length)
+ texCoordNode = texCoordNode._cf.texCoord.nodes[0];
+ }
+ if (texCoordNode) {
+ if (texCoordNode._vf.point) {
+ hasTexCoord = true;
+ texCoords = texCoordNode._vf.point;
+
+ if (x3dom.isa(texCoordNode, x3dom.nodeTypes.TextureCoordinate3D)) {
+ numTexComponents = 3;
+ }
+ }
+ else if (texCoordNode._vf.mode) {
+ texMode = texCoordNode._vf.mode;
+ }
+ }
+ else {
+ hasTexCoord = false;
+ }
+
+ var numColComponents = 3;
+ var colorNode = this._cf.color.node;
+ if (colorNode) {
+ hasColor = true;
+ colors = colorNode._vf.color;
+
+ if (x3dom.isa(colorNode, x3dom.nodeTypes.ColorRGBA)) {
+ numColComponents = 4;
+ }
+ }
+ else {
+ hasColor = false;
+ }
+
+ this._mesh._indices[0] = [];
+ this._mesh._positions[0] = [];
+ this._mesh._normals[0] = [];
+ this._mesh._texCoords[0] = [];
+ this._mesh._colors[0] = [];
+
+ var i, t, cnt, faceCnt, posMax;
+ var p0, p1, p2, n0, n1, n2, t0, t1, t2, c0, c1, c2;
+
+ // if positions array too short add degenerate triangle
+ while (positions.length % 3 > 0) {
+ positions.push(positions.length-1);
+ }
+ posMax = positions.length;
+
+ if (!normPerVert || posMax > x3dom.Utils.maxIndexableCoords)
+ {
+ t = 0;
+ cnt = 0;
+ faceCnt = 0;
+ this._mesh._multiIndIndices = [];
+ this._mesh._posSize = positions.length;
+
+ for (i=0; i < indexes.length; ++i)
+ {
+ // Convert non-triangular polygons to a triangle fan
+ // (TODO: this assumes polygons are convex)
+
+ if ((i > 0) && (i % 3 === 0 )) {
+ t = 0;
+ faceCnt++;
+ }
+
+ //TODO: OPTIMIZE but think about cache coherence regarding arrays!!!
+ switch (t)
+ {
+ case 0:
+ p0 = +indexes[i];
+ if (normPerVert) {
+ n0 = p0;
+ } else if (!normPerVert) {
+ n0 = faceCnt;
+ }
+ t0 = p0;
+ if (colPerVert) {
+ c0 = p0;
+ } else if (!colPerVert) {
+ c0 = faceCnt;
+ }
+ t = 1;
+ break;
+ case 1:
+ p1 = +indexes[i];
+ if (normPerVert) {
+ n1 = p1;
+ } else if (!normPerVert) {
+ n1 = faceCnt;
+ }
+ t1 = p1;
+ if (colPerVert) {
+ c1 = p1;
+ } else if (!colPerVert) {
+ c1 = faceCnt;
+ }
+ t = 2;
+ break;
+ case 2:
+ p2 = +indexes[i];
+ if (normPerVert) {
+ n2 = p2;
+ } else if (!normPerVert) {
+ n2 = faceCnt;
+ }
+ t2 = p2;
+ if (colPerVert) {
+ c2 = p2;
+ } else if (!colPerVert) {
+ c2 = faceCnt;
+ }
+ t = 3;
+
+ this._mesh._indices[0].push(cnt++, cnt++, cnt++);
+
+ this._mesh._positions[0].push(positions[p0].x);
+ this._mesh._positions[0].push(positions[p0].y);
+ this._mesh._positions[0].push(positions[p0].z);
+ this._mesh._positions[0].push(positions[p1].x);
+ this._mesh._positions[0].push(positions[p1].y);
+ this._mesh._positions[0].push(positions[p1].z);
+ this._mesh._positions[0].push(positions[p2].x);
+ this._mesh._positions[0].push(positions[p2].y);
+ this._mesh._positions[0].push(positions[p2].z);
+
+ if (hasNormal) {
+ this._mesh._normals[0].push(normals[n0].x);
+ this._mesh._normals[0].push(normals[n0].y);
+ this._mesh._normals[0].push(normals[n0].z);
+ this._mesh._normals[0].push(normals[n1].x);
+ this._mesh._normals[0].push(normals[n1].y);
+ this._mesh._normals[0].push(normals[n1].z);
+ this._mesh._normals[0].push(normals[n2].x);
+ this._mesh._normals[0].push(normals[n2].y);
+ this._mesh._normals[0].push(normals[n2].z);
+ }
+ else {
+ this._mesh._multiIndIndices.push(p0, p1, p2);
+ //this._mesh._multiIndIndices.push(cnt-3, cnt-2, cnt-1);
+ }
+
+ if (hasColor) {
+ this._mesh._colors[0].push(colors[c0].r);
+ this._mesh._colors[0].push(colors[c0].g);
+ this._mesh._colors[0].push(colors[c0].b);
+ if (numColComponents === 4) {
+ this._mesh._colors[0].push(colors[c0].a);
+ }
+ this._mesh._colors[0].push(colors[c1].r);
+ this._mesh._colors[0].push(colors[c1].g);
+ this._mesh._colors[0].push(colors[c1].b);
+ if (numColComponents === 4) {
+ this._mesh._colors[0].push(colors[c1].a);
+ }
+ this._mesh._colors[0].push(colors[c2].r);
+ this._mesh._colors[0].push(colors[c2].g);
+ this._mesh._colors[0].push(colors[c2].b);
+ if (numColComponents === 4) {
+ this._mesh._colors[0].push(colors[c2].a);
+ }
+ }
+
+ if (hasTexCoord) {
+ this._mesh._texCoords[0].push(texCoords[t0].x);
+ this._mesh._texCoords[0].push(texCoords[t0].y);
+ if (numTexComponents === 3) {
+ this._mesh._texCoords[0].push(texCoords[t0].z);
+ }
+ this._mesh._texCoords[0].push(texCoords[t1].x);
+ this._mesh._texCoords[0].push(texCoords[t1].y);
+ if (numTexComponents === 3) {
+ this._mesh._texCoords[0].push(texCoords[t1].z);
+ }
+ this._mesh._texCoords[0].push(texCoords[t2].x);
+ this._mesh._texCoords[0].push(texCoords[t2].y);
+ if (numTexComponents === 3) {
+ this._mesh._texCoords[0].push(texCoords[t2].z);
+ }
+ }
+
+ //faceCnt++;
+ break;
+ default:
+ }
+ }
+
+ if (!hasNormal) {
+ this._mesh.calcNormals(normPerVert ? Math.PI : 0);
+ }
+ if (!hasTexCoord) {
+ this._mesh.calcTexCoords(texMode);
+ }
+
+ this._mesh.splitMesh();
+
+ //x3dom.debug.logInfo(this._mesh._indices.length);
+ } // if isMulti
+ else
+ {
+ faceCnt = 0;
+ for (i=0; i<indexes.length; i++)
+ {
+ if ((i > 0) && (i % 3 === 0 )) {
+ faceCnt++;
+ }
+
+ this._mesh._indices[0].push(indexes[i]);
+
+ if(!normPerVert && hasNormal) {
+ this._mesh._normals[0].push(normals[faceCnt].x);
+ this._mesh._normals[0].push(normals[faceCnt].y);
+ this._mesh._normals[0].push(normals[faceCnt].z);
+ }
+ if(!colPerVert && hasColor) {
+ this._mesh._colors[0].push(colors[faceCnt].r);
+ this._mesh._colors[0].push(colors[faceCnt].g);
+ this._mesh._colors[0].push(colors[faceCnt].b);
+ if (numColComponents === 4) {
+ this._mesh._colors[0].push(colors[faceCnt].a);
+ }
+ }
+ }
+
+ this._mesh._positions[0] = positions.toGL();
+
+ if (hasNormal) {
+ this._mesh._normals[0] = normals.toGL();
+ }
+ else {
+ this._mesh.calcNormals(normPerVert ? Math.PI : 0);
+ }
+
+ if (hasTexCoord) {
+ this._mesh._texCoords[0] = texCoords.toGL();
+ this._mesh._numTexComponents = numTexComponents;
+ }
+ else {
+ this._mesh.calcTexCoords(texMode);
+ }
+
+ if (hasColor && colPerVert) {
+ this._mesh._colors[0] = colors.toGL();
+ this._mesh._numColComponents = numColComponents;
+ }
+ }
+
+ this.invalidateVolume();
+
+ this._mesh._numFaces = 0;
+ this._mesh._numCoords = 0;
+ for (i=0; i<this._mesh._indices.length; i++) {
+ this._mesh._numFaces += this._mesh._indices[i].length / 3;
+ this._mesh._numCoords += this._mesh._positions[i].length / 3;
+ }
+
+ var time1 = new Date().getTime() - time0;
+ //x3dom.debug.logInfo("Mesh load time: " + time1 + " ms");
+ },
+
+ fieldChanged: function(fieldName)
+ {
+ var pnts = this._cf.coord.node._vf.point;
+
+ if ( pnts.length > x3dom.Utils.maxIndexableCoords ) // are there other problematic cases?
+ {
+ // TODO; implement
+ x3dom.debug.logWarning("IndexedTriangleSet: fieldChanged with " +
+ "too many coordinates not yet implemented!");
+ return;
+ }
+
+ if (fieldName == "coord")
+ {
+ this._mesh._positions[0] = pnts.toGL();
+
+ // tells the mesh that its bbox requires update
+ this.invalidateVolume();
+
+ Array.forEach(this._parentNodes, function (node) {
+ node._dirty.positions = true;
+ node.invalidateVolume();
+ });
+ }
+ else if (fieldName == "color")
+ {
+ pnts = this._cf.color.node._vf.color;
+
+ if (this._vf.colorPerVertex) {
+
+ this._mesh._colors[0] = pnts.toGL();
+
+ } else if (!this._vf.colorPerVertex) {
+
+ var faceCnt = 0;
+ var numColComponents = 3;
+ if (x3dom.isa(this._cf.color.node, x3dom.nodeTypes.ColorRGBA)) {
+ numColComponents = 4;
+ }
+
+ this._mesh._colors[0] = [];
+
+ var indexes = this._vf.index;
+ for (var i=0; i < indexes.length; ++i)
+ {
+ if ((i > 0) && (i % 3 === 0 )) {
+ faceCnt++;
+ }
+
+ this._mesh._colors[0].push(pnts[faceCnt].r);
+ this._mesh._colors[0].push(pnts[faceCnt].g);
+ this._mesh._colors[0].push(pnts[faceCnt].b);
+ if (numColComponents === 4) {
+ this._mesh._colors[0].push(pnts[faceCnt].a);
+ }
+ }
+ }
+ Array.forEach(this._parentNodes, function (node) {
+ node._dirty.colors = true;
+ });
+ }
+ else if (fieldName == "normal")
+ {
+ pnts = this._cf.normal.node._vf.vector;
+
+ if (this._vf.normalPerVertex) {
+
+ this._mesh._normals[0] = pnts.toGL();
+
+ } else if (!this._vf.normalPerVertex) {
+
+ var indexes = this._vf.index;
+ this._mesh._normals[0] = [];
+
+ var faceCnt = 0;
+ for (var i=0; i < indexes.length; ++i)
+ {
+ if ((i > 0) && (i % 3 === 0 )) {
+ faceCnt++;
+ }
+
+ this._mesh._normals[0].push(pnts[faceCnt].x);
+ this._mesh._normals[0].push(pnts[faceCnt].y);
+ this._mesh._normals[0].push(pnts[faceCnt].z);
+ }
+ }
+
+ Array.forEach(this._parentNodes, function (node) {
+ node._dirty.normals = true;
+ });
+ }
+ else if (fieldName == "texCoord")
+ {
+ var texCoordNode = this._cf.texCoord.node;
+ if (x3dom.isa(texCoordNode, x3dom.nodeTypes.MultiTextureCoordinate)) {
+ if (texCoordNode._cf.texCoord.nodes.length)
+ texCoordNode = texCoordNode._cf.texCoord.nodes[0];
+ }
+ pnts = texCoordNode._vf.point;
+
+ this._mesh._texCoords[0] = pnts.toGL();
+
+ Array.forEach(this._parentNodes, function (node) {
+ node._dirty.texcoords = true;
+ });
+ }
+ // TODO: index
+ }
+ }
+ )
+);
+
+/** @namespace x3dom.nodeTypes */
+/*
+ * X3DOM JavaScript Library
+ * http://www.x3dom.org
+ *
+ * (C)2009 Fraunhofer IGD, Darmstadt, Germany
+ * Dual licensed under the MIT and GPL
+ */
+
+/* ### IndexedTriangleStripSet ### */
+x3dom.registerNodeType(
+ "IndexedTriangleStripSet",
+ "Rendering",
+ defineClass(x3dom.nodeTypes.X3DComposedGeometryNode,
+
+ /**
+ * Constructor for IndexedTriangleStripSet
+ * @constructs x3dom.nodeTypes.IndexedTriangleStripSet
+ * @x3d 3.3
+ * @component Rendering
+ * @status experimental
+ * @extends x3dom.nodeTypes.X3DComposedGeometryNode
+ * @param {Object} [ctx=null] - context object, containing initial settings like namespace
+ * @classdesc IndexedTriangleStripSet is a geometry node that can contain a Color, Coordinate, Normal and TextureCoordinate node.
+ * Hint: insert a Shape node before adding geometry or Appearance. You can also substitute a type-matched ProtoInstance for content.
+ */
+ function (ctx) {
+ x3dom.nodeTypes.IndexedTriangleStripSet.superClass.call(this, ctx);
+
+
+ /**
+ * Index specifies triangles by connecting Coordinate vertices.
+ * @var {x3dom.fields.MFInt32} index
+ * @range [0, inf]
+ * @memberof x3dom.nodeTypes.IndexedTriangleStripSet
+ * @initvalue []
+ * @field x3d
+ * @instance
+ */
+ this.addField_MFInt32(ctx, 'index', []);
+
+ this._hasIndexOffset = false;
+ this._indexOffset = null;
+
+ },
+ {
+ hasIndexOffset: function() {
+ return this._hasIndexOffset;
+ },
+
+ nodeChanged: function()
+ {
+ this.handleAttribs(); // check if method is still functional
+
+ var hasNormal = false, hasTexCoord = false, hasColor = false;
+
+ var colPerVert = this._vf.colorPerVertex;
+ var normPerVert = this._vf.normalPerVertex;
+
+ var indexes = this._vf.index;
+ var positions, normals, texCoords, colors;
+
+ var coordNode = this._cf.coord.node;
+ x3dom.debug.assert(coordNode);
+ positions = coordNode._vf.point;
+
+ var normalNode = this._cf.normal.node;
+ if (normalNode) {
+ hasNormal = true;
+ normals = normalNode._vf.vector;
+ }
+ else {
+ hasNormal = false;
+ }
+
+ var texMode = "", numTexComponents = 2;
+ var texCoordNode = this._cf.texCoord.node;
+ if (x3dom.isa(texCoordNode, x3dom.nodeTypes.MultiTextureCoordinate)) {
+ if (texCoordNode._cf.texCoord.nodes.length)
+ texCoordNode = texCoordNode._cf.texCoord.nodes[0];
+ }
+ if (texCoordNode) {
+ if (texCoordNode._vf.point) {
+ hasTexCoord = true;
+ texCoords = texCoordNode._vf.point;
+
+ if (x3dom.isa(texCoordNode, x3dom.nodeTypes.TextureCoordinate3D)) {
+ numTexComponents = 3;
+ }
+ }
+ else if (texCoordNode._vf.mode) {
+ texMode = texCoordNode._vf.mode;
+ }
+ }
+ else {
+ hasTexCoord = false;
+ }
+ this._mesh._numTexComponents = numTexComponents;
+
+ var numColComponents = 3;
+ var colorNode = this._cf.color.node;
+ if (colorNode) {
+ hasColor = true;
+ colors = colorNode._vf.color;
+
+ if (x3dom.isa(colorNode, x3dom.nodeTypes.ColorRGBA)) {
+ numColComponents = 4;
+ }
+ }
+ else {
+ hasColor = false;
+ }
+ this._mesh._numColComponents = numColComponents;
+
+ this._mesh._indices[0] = [];
+ this._mesh._positions[0] = [];
+ this._mesh._normals[0] = [];
+ this._mesh._texCoords[0] = [];
+ this._mesh._colors[0] = [];
+
+ this.invalidateVolume();
+ this._mesh._numFaces = 0;
+ this._mesh._numCoords = 0;
+
+ var faceCnt = 0, cnt = 0;
+
+ if (hasNormal && positions.length <= x3dom.Utils.maxIndexableCoords)
+ {
+ this._hasIndexOffset = true;
+ this._indexOffset = [];
+ this._mesh._primType = 'TRIANGLESTRIP';
+
+ var indexOffset = [ 0 ];
+
+ for (i=0; i<indexes.length; i++)
+ {
+ if (indexes[i] == -1) {
+ faceCnt++;
+ indexOffset.push(this._mesh._indices[0].length);
+ }
+ else {
+ this._mesh._indices[0].push(+indexes[i]);
+
+ if(!normPerVert) {
+ this._mesh._normals[0].push(normals[faceCnt].x);
+ this._mesh._normals[0].push(normals[faceCnt].y);
+ this._mesh._normals[0].push(normals[faceCnt].z);
+ }
+ if(!colPerVert) {
+ this._mesh._colors[0].push(colors[faceCnt].r);
+ this._mesh._colors[0].push(colors[faceCnt].g);
+ this._mesh._colors[0].push(colors[faceCnt].b);
+ if (numColComponents === 4) {
+ this._mesh._colors[0].push(colors[faceCnt].a);
+ }
+ }
+ }
+ }
+
+ this._mesh._positions[0] = positions.toGL();
+
+ if(normPerVert) {
+ this._mesh._normals[0] = normals.toGL();
+ }
+
+ if (hasTexCoord) {
+ this._mesh._texCoords[0] = texCoords.toGL();
+ this._mesh._numTexComponents = numTexComponents;
+ }
+ else {
+ x3dom.debug.logWarning("IndexedTriangleStripSet: no texCoords given and won't calculate!");
+ }
+
+ if (hasColor) {
+ if(colPerVert) {
+ this._mesh._colors[0] = colors.toGL();
+ }
+ this._mesh._numColComponents = numColComponents;
+ }
+
+ for (i=1; i<indexOffset.length; i++) {
+ var triCnt = indexOffset[i] - indexOffset[i-1];
+ this._indexOffset.push( {
+ count: triCnt,
+ offset: 2 * indexOffset[i-1]
+ } );
+
+ this._mesh._numFaces += (triCnt - 2);
+ }
+ this._mesh._numCoords = this._mesh._positions[0].length / 3;
+ }
+ else
+ {
+ this._hasIndexOffset = false;
+
+ var p1, p2 , p3, n1, n2, n3, t1, t2, t3, c1, c2, c3;
+
+ var swapOrder = false;
+
+ for (var i=1; i < indexes.length-2; ++i)
+ {
+ if (indexes[i+1] == -1) {
+ i = i+2;
+ faceCnt++;
+ continue;
+ }
+
+ // care for counterclockwise point order
+ if (swapOrder) {
+ p1 = indexes[i];
+ p2 = indexes[i-1];
+ p3 = indexes[i+1];
+ }
+ else {
+ p1 = indexes[i-1];
+ p2 = indexes[i];
+ p3 = indexes[i+1];
+ }
+ swapOrder = !swapOrder;
+
+ if (normPerVert) {
+ n1 = p1;
+ n2 = p2;
+ n3 = p3;
+ } else if (!normPerVert) {
+ n1 = n2 = n3 = faceCnt;
+ }
+
+ t1 = p1;
+ t2 = p2;
+ t3 = p3;
+
+ if (colPerVert) {
+ c1 = p1;
+ c2 = p2;
+ c3 = p3;
+ } else if (!colPerVert) {
+ c1 = c2 = c3 = faceCnt;
+ }
+
+ this._mesh._indices[0].push(cnt++, cnt++, cnt++);
+
+ this._mesh._positions[0].push(positions[p1].x);
+ this._mesh._positions[0].push(positions[p1].y);
+ this._mesh._positions[0].push(positions[p1].z);
+ this._mesh._positions[0].push(positions[p2].x);
+ this._mesh._positions[0].push(positions[p2].y);
+ this._mesh._positions[0].push(positions[p2].z);
+ this._mesh._positions[0].push(positions[p3].x);
+ this._mesh._positions[0].push(positions[p3].y);
+ this._mesh._positions[0].push(positions[p3].z);
+
+ if (hasNormal) {
+ this._mesh._normals[0].push(normals[n1].x);
+ this._mesh._normals[0].push(normals[n1].y);
+ this._mesh._normals[0].push(normals[n1].z);
+ this._mesh._normals[0].push(normals[n2].x);
+ this._mesh._normals[0].push(normals[n2].y);
+ this._mesh._normals[0].push(normals[n2].z);
+ this._mesh._normals[0].push(normals[n3].x);
+ this._mesh._normals[0].push(normals[n3].y);
+ this._mesh._normals[0].push(normals[n3].z);
+ }
+
+ if (hasColor) {
+ this._mesh._colors[0].push(colors[c1].r);
+ this._mesh._colors[0].push(colors[c1].g);
+ this._mesh._colors[0].push(colors[c1].b);
+ if (numColComponents === 4) {
+ this._mesh._colors[0].push(colors[c1].a);
+ }
+ this._mesh._colors[0].push(colors[c2].r);
+ this._mesh._colors[0].push(colors[c2].g);
+ this._mesh._colors[0].push(colors[c2].b);
+ if (numColComponents === 4) {
+ this._mesh._colors[0].push(colors[c2].a);
+ }
+ this._mesh._colors[0].push(colors[c3].r);
+ this._mesh._colors[0].push(colors[c3].g);
+ this._mesh._colors[0].push(colors[c3].b);
+ if (numColComponents === 4) {
+ this._mesh._colors[0].push(colors[c3].a);
+ }
+ }
+
+ if (hasTexCoord) {
+ this._mesh._texCoords[0].push(texCoords[t1].x);
+ this._mesh._texCoords[0].push(texCoords[t1].y);
+ if (numTexComponents === 3) {
+ this._mesh._texCoords[0].push(texCoords[t1].z);
+ }
+ this._mesh._texCoords[0].push(texCoords[t2].x);
+ this._mesh._texCoords[0].push(texCoords[t2].y);
+ if (numTexComponents === 3) {
+ this._mesh._texCoords[0].push(texCoords[t2].z);
+ }
+ this._mesh._texCoords[0].push(texCoords[t3].x);
+ this._mesh._texCoords[0].push(texCoords[t3].y);
+ if (numTexComponents === 3) {
+ this._mesh._texCoords[0].push(texCoords[t3].z);
+ }
+ }
+ }
+
+ if (!hasNormal) {
+ this._mesh.calcNormals(Math.PI);
+ }
+
+ if (!hasTexCoord) {
+ this._mesh.calcTexCoords(texMode);
+ }
+
+ this._mesh.splitMesh();
+
+ this.invalidateVolume();
+
+ for (i=0; i<this._mesh._indices.length; i++) {
+ this._mesh._numFaces += this._mesh._indices[i].length / 3;
+ this._mesh._numCoords += this._mesh._positions[i].length / 3;
+ }
+ }
+ },
+
+ fieldChanged: function(fieldName)
+ {
+ if (fieldName != "coord" && fieldName != "normal" &&
+ fieldName != "texCoord" && fieldName != "color")
+ {
+ x3dom.debug.logWarning("IndexedTriangleStripSet: fieldChanged for " +
+ fieldName + " not yet implemented!");
+ return;
+ }
+
+ var pnts = this._cf.coord.node._vf.point;
+
+ if ((this._cf.normal.node === null) || (pnts.length > x3dom.Utils.maxIndexableCoords))
+ {
+ if (fieldName == "coord") {
+ this._mesh._positions[0] = [];
+ this._mesh._indices[0] =[];
+ this._mesh._normals[0] = [];
+ this._mesh._texCoords[0] =[];
+
+ var hasNormal = false, hasTexCoord = false, hasColor = false;
+
+ var colPerVert = this._vf.colorPerVertex;
+ var normPerVert = this._vf.normalPerVertex;
+
+ var indexes = this._vf.index;
+ var positions, normals, texCoords, colors;
+
+ var coordNode = this._cf.coord.node;
+ x3dom.debug.assert(coordNode);
+ positions = coordNode._vf.point;
+
+ var normalNode = this._cf.normal.node;
+ if (normalNode) {
+ hasNormal = true;
+ normals = normalNode._vf.vector;
+ }
+ else {
+ hasNormal = false;
+ }
+
+ var texMode = "", numTexComponents = 2;
+ var texCoordNode = this._cf.texCoord.node;
+ if (x3dom.isa(texCoordNode, x3dom.nodeTypes.MultiTextureCoordinate)) {
+ if (texCoordNode._cf.texCoord.nodes.length)
+ texCoordNode = texCoordNode._cf.texCoord.nodes[0];
+ }
+ if (texCoordNode) {
+ if (texCoordNode._vf.point) {
+ hasTexCoord = true;
+ texCoords = texCoordNode._vf.point;
+
+ if (x3dom.isa(texCoordNode, x3dom.nodeTypes.TextureCoordinate3D)) {
+ numTexComponents = 3;
+ }
+ }
+ else if (texCoordNode._vf.mode) {
+ texMode = texCoordNode._vf.mode;
+ }
+ }
+ else {
+ hasTexCoord = false;
+ }
+ this._mesh._numTexComponents = numTexComponents;
+
+ var numColComponents = 3;
+ var colorNode = this._cf.color.node;
+ if (colorNode) {
+ hasColor = true;
+ colors = colorNode._vf.color;
+
+ if (x3dom.isa(colorNode, x3dom.nodeTypes.ColorRGBA)) {
+ numColComponents = 4;
+ }
+ }
+ else {
+ hasColor = false;
+ }
+ this._mesh._numColComponents = numColComponents;
+
+ this._mesh._indices[0] = [];
+ this._mesh._positions[0] = [];
+ this._mesh._normals[0] = [];
+ this._mesh._texCoords[0] = [];
+ this._mesh._colors[0] = [];
+
+ var faceCnt = 0, cnt = 0;
+ var p1, p2 , p3, n1, n2, n3, t1, t2, t3, c1, c2, c3;
+ var swapOrder = false;
+
+ if ( hasNormal || hasTexCoord || hasColor) {
+
+ for (var i=1; i < indexes.length-2; ++i)
+ {
+ if (indexes[i+1] == -1) {
+ i = i+2;
+ faceCnt++;
+ continue;
+ }
+
+ if (swapOrder) {
+ p1 = indexes[i];
+ p2 = indexes[i-1];
+ p3 = indexes[i+1];
+ }
+ else {
+ p1 = indexes[i-1];
+ p2 = indexes[i];
+ p3 = indexes[i+1];
+ }
+ swapOrder = !swapOrder;
+
+ if (normPerVert) {
+ n1 = p1;
+ n2 = p2;
+ n3 = p3;
+ } else if (!normPerVert) {
+ n1 = n2 = n3 = faceCnt;
+ }
+
+ t1 = p1;
+ t2 = p2;
+ t3 = p3;
+
+ if (colPerVert) {
+ c1 = p1;
+ c2 = p2;
+ c3 = p3;
+ } else if (!colPerVert) {
+ c1 = c2 = c3 = faceCnt;
+ }
+
+ this._mesh._indices[0].push(cnt++, cnt++, cnt++);
+
+ this._mesh._positions[0].push(positions[p1].x);
+ this._mesh._positions[0].push(positions[p1].y);
+ this._mesh._positions[0].push(positions[p1].z);
+ this._mesh._positions[0].push(positions[p2].x);
+ this._mesh._positions[0].push(positions[p2].y);
+ this._mesh._positions[0].push(positions[p2].z);
+ this._mesh._positions[0].push(positions[p3].x);
+ this._mesh._positions[0].push(positions[p3].y);
+ this._mesh._positions[0].push(positions[p3].z);
+
+ if (hasNormal) {
+ this._mesh._normals[0].push(normals[n1].x);
+ this._mesh._normals[0].push(normals[n1].y);
+ this._mesh._normals[0].push(normals[n1].z);
+ this._mesh._normals[0].push(normals[n2].x);
+ this._mesh._normals[0].push(normals[n2].y);
+ this._mesh._normals[0].push(normals[n2].z);
+ this._mesh._normals[0].push(normals[n3].x);
+ this._mesh._normals[0].push(normals[n3].y);
+ this._mesh._normals[0].push(normals[n3].z);
+ }
+
+ if (hasColor) {
+ this._mesh._colors[0].push(colors[c1].r);
+ this._mesh._colors[0].push(colors[c1].g);
+ this._mesh._colors[0].push(colors[c1].b);
+ if (numColComponents === 4) {
+ this._mesh._colors[0].push(colors[c1].a);
+ }
+ this._mesh._colors[0].push(colors[c2].r);
+ this._mesh._colors[0].push(colors[c2].g);
+ this._mesh._colors[0].push(colors[c2].b);
+ if (numColComponents === 4) {
+ this._mesh._colors[0].push(colors[c2].a);
+ }
+ this._mesh._colors[0].push(colors[c3].r);
+ this._mesh._colors[0].push(colors[c3].g);
+ this._mesh._colors[0].push(colors[c3].b);
+ if (numColComponents === 4) {
+ this._mesh._colors[0].push(colors[c3].a);
+ }
+ }
+
+ if (hasTexCoord) {
+ this._mesh._texCoords[0].push(texCoords[t1].x);
+ this._mesh._texCoords[0].push(texCoords[t1].y);
+ if (numTexComponents === 3) {
+ this._mesh._texCoords[0].push(texCoords[t1].z);
+ }
+ this._mesh._texCoords[0].push(texCoords[t2].x);
+ this._mesh._texCoords[0].push(texCoords[t2].y);
+ if (numTexComponents === 3) {
+ this._mesh._texCoords[0].push(texCoords[t2].z);
+ }
+ this._mesh._texCoords[0].push(texCoords[t3].x);
+ this._mesh._texCoords[0].push(texCoords[t3].y);
+ if (numTexComponents === 3) {
+ this._mesh._texCoords[0].push(texCoords[t3].z);
+ }
+ }
+ }
+
+ if (!hasNormal) {
+ this._mesh.calcNormals(Math.PI);
+ }
+
+ if (!hasTexCoord) {
+ this._mesh.calcTexCoords(texMode);
+ }
+
+ this._mesh.splitMesh();
+
+ } else {
+ var swapOrder = false;
+ for (var i = 1; i < indexes.length; ++i)
+ {
+ if (indexes[i+1] == -1) {
+ i = i+2;
+ continue;
+ }
+
+ if (swapOrder) {
+ this._mesh._indices[0].push(indexes[i]);
+ this._mesh._indices[0].push(indexes[i-1]);
+ this._mesh._indices[0].push(indexes[i+1]);
+ }
+ else {
+ this._mesh._indices[0].push(indexes[i-1]);
+ this._mesh._indices[0].push(indexes[i]);
+ this._mesh._indices[0].push(indexes[i+1]);
+ }
+ swapOrder = !swapOrder;
+ }
+
+ this._mesh._positions[0] = positions.toGL();
+
+ if (hasNormal) {
+ this._mesh._normals[0] = normals.toGL();
+ }
+ else {
+ this._mesh.calcNormals(Math.PI);
+ }
+ if (hasTexCoord) {
+ this._mesh._texCoords[0] = texCoords.toGL();
+ this._mesh._numTexComponents = numTexComponents;
+ }
+ else {
+ this._mesh.calcTexCoords(texMode);
+ }
+ if (hasColor) {
+ this._mesh._colors[0] = colors.toGL();
+ this._mesh._numColComponents = numColComponents;
+ }
+
+ }
+
+ this.invalidateVolume();
+ this._mesh._numFaces = 0;
+ this._mesh._numCoords = 0;
+
+ for (i=0; i<this._mesh._indices.length; i++) {
+ this._mesh._numFaces += this._mesh._indices[i].length / 3;
+ this._mesh._numCoords += this._mesh._positions[i].length / 3;
+ }
+
+ Array.forEach(this._parentNodes, function (node) {
+ node.setAllDirty();
+ node.invalidateVolume();
+ });
+ }
+ else if (fieldName == "color") {
+ var col = this._cf.color.node._vf.color;
+ var faceCnt = 0;
+ var c1 = c2 = c3 = 0;
+
+ var numColComponents = 3;
+
+ if (x3dom.isa(this._cf.color.node, x3dom.nodeTypes.ColorRGBA)) {
+ numColComponents = 4;
+ }
+
+ this._mesh._colors[0] = [];
+
+ var indexes = this._vf.index;
+ var swapOrder = false;
+
+ for (i=1; i < indexes.length-2; ++i)
+ {
+ if (indexes[i+1] == -1) {
+ i = i+2;
+ faceCnt++;
+ continue;
+ }
+
+ if (this._vf.colorPerVertex) {
+ if (swapOrder) {
+ c1 = indexes[i];
+ c2 = indexes[i-1];
+ c3 = indexes[i+1];
+ }
+ else {
+ c1 = indexes[i-1];
+ c2 = indexes[i];
+ c3 = indexes[i+1];
+ }
+ swapOrder = !swapOrder;
+ } else if (!this._vf.colorPerVertex) {
+ c1 = c2 = c3 = faceCnt;
+ }
+ this._mesh._colors[0].push(col[c1].r);
+ this._mesh._colors[0].push(col[c1].g);
+ this._mesh._colors[0].push(col[c1].b);
+ if (numColComponents === 4) {
+ this._mesh._colors[0].push(col[c1].a);
+ }
+ this._mesh._colors[0].push(col[c2].r);
+ this._mesh._colors[0].push(col[c2].g);
+ this._mesh._colors[0].push(col[c2].b);
+ if (numColComponents === 4) {
+ this._mesh._colors[0].push(col[c2].a);
+ }
+ this._mesh._colors[0].push(col[c3].r);
+ this._mesh._colors[0].push(col[c3].g);
+ this._mesh._colors[0].push(col[c3].b);
+ if (numColComponents === 4) {
+ this._mesh._colors[0].push(col[c3].a);
+ }
+ }
+
+ Array.forEach(this._parentNodes, function (node) {
+ node._dirty.colors = true;
+ });
+ }
+ else if (fieldName == "normal") {
+ var nor = this._cf.normal.node._vf.vector;
+ var faceCnt = 0;
+ var n1 = n2 = n3 = 0;
+
+ this._mesh._normals[0] = [];
+
+ var indexes = this._vf.index;
+ var swapOrder = false;
+
+ for (i=1; i < indexes.length-2; ++i)
+ {
+ if (indexes[i+1] == -1) {
+ i = i+2;
+ faceCnt++;
+ continue;
+ }
+
+ if (this._vf.normalPerVertex) {
+ if (swapOrder) {
+ n1 = indexes[i];
+ n2 = indexes[i-1];
+ n3 = indexes[i+1];
+ }
+ else {
+ n1 = indexes[i-1];
+ n2 = indexes[i];
+ n3 = indexes[i+1];
+ }
+ swapOrder = !swapOrder;
+ } else if (!this._vf.normalPerVertex) {
+ n1 = n2 = n3 = faceCnt;
+ }
+ this._mesh._normals[0].push(nor[n1].x);
+ this._mesh._normals[0].push(nor[n1].y);
+ this._mesh._normals[0].push(nor[n1].z);
+ this._mesh._normals[0].push(nor[n2].x);
+ this._mesh._normals[0].push(nor[n2].y);
+ this._mesh._normals[0].push(nor[n2].z);
+ this._mesh._normals[0].push(nor[n3].x);
+ this._mesh._normals[0].push(nor[n3].y);
+ this._mesh._normals[0].push(nor[n3].z);
+ }
+
+ Array.forEach(this._parentNodes, function (node) {
+ node._dirty.normals = true;
+ });
+ }
+ else if (fieldName == "texCoord") {
+ var texCoordNode = this._cf.texCoord.node;
+ if (x3dom.isa(texCoordNode, x3dom.nodeTypes.MultiTextureCoordinate)) {
+ if (texCoordNode._cf.texCoord.nodes.length)
+ texCoordNode = texCoordNode._cf.texCoord.nodes[0];
+ }
+ var tex = texCoordNode._vf.point;
+ var t1 = t2 = t3 = 0;
+
+ var numTexComponents = 2;
+
+ if (x3dom.isa(texCoordNode, x3dom.nodeTypes.TextureCoordinate3D)) {
+ numTexComponents = 3;
+ }
+
+ this._mesh._texCoords[0] = [];
+ var indexes = this._vf.index;
+ var swapOrder = false;
+
+ for (i=1; i < indexes.length-2; ++i)
+ {
+ if (indexes[i+1] == -1) {
+ i = i+2;
+ continue;
+ }
+
+ if (swapOrder) {
+ t1 = indexes[i];
+ t2 = indexes[i-1];
+ t3 = indexes[i+1];
+ }
+ else {
+ t1 = indexes[i-1];
+ t2 = indexes[i];
+ t3 = indexes[i+1];
+ }
+ swapOrder = !swapOrder;
+
+ this._mesh._texCoords[0].push(tex[t1].x);
+ this._mesh._texCoords[0].push(tex[t1].y);
+ if (numTexComponents === 3) {
+ this._mesh._texCoords[0].push(tex[t1].z);
+ }
+ this._mesh._texCoords[0].push(tex[t2].x);
+ this._mesh._texCoords[0].push(tex[t2].y);
+ if (numTexComponents === 3) {
+ this._mesh._texCoords[0].tex(col[t2].z);
+ }
+ this._mesh._texCoords[0].push(tex[t3].x);
+ this._mesh._texCoords[0].push(tex[t3].y);
+ if (numTexComponents === 3) {
+ this._mesh._texCoords[0].push(tex[t3].z);
+ }
+ }
+
+ Array.forEach(this._parentNodes, function (node) {
+ node._dirty.texcoords = true;
+ });
+ }
+ }
+ else
+ {
+ if (fieldName == "coord")
+ {
+ this._mesh._positions[0] = pnts.toGL();
+
+ // tells the mesh that its bbox requires update
+ this.invalidateVolume();
+
+ Array.forEach(this._parentNodes, function (node) {
+ node._dirty.positions = true;
+ node.invalidateVolume();
+ });
+ }
+ else if (fieldName == "color")
+ {
+ pnts = this._cf.color.node._vf.color;
+
+ if (this._vf.colorPerVertex) {
+
+ this._mesh._colors[0] = pnts.toGL();
+
+ } else if (!this._vf.colorPerVertex) {
+
+ var faceCnt = 0;
+ var numColComponents = 3;
+
+ if (x3dom.isa(this._cf.color.node, x3dom.nodeTypes.ColorRGBA)) {
+ numColComponents = 4;
+ }
+
+ this._mesh._colors[0] = [];
+
+ var indexes = this._vf.index;
+ for (i=0; i < indexes.length; ++i)
+ {
+ if (indexes[i] == -1) {
+ faceCnt++;
+ continue;
+ }
+
+ this._mesh._colors[0].push(pnts[faceCnt].r);
+ this._mesh._colors[0].push(pnts[faceCnt].g);
+ this._mesh._colors[0].push(pnts[faceCnt].b);
+ if (numColComponents === 4) {
+ this._mesh._colors[0].push(pnts[faceCnt].a);
+ }
+ }
+ }
+
+ Array.forEach(this._parentNodes, function (node) {
+ node._dirty.colors = true;
+ });
+ }
+ else if (fieldName == "normal")
+ {
+ pnts = this._cf.normal.node._vf.vector;
+
+ if (this._vf.normalPerVertex) {
+
+ this._mesh._normals[0] = pnts.toGL();
+
+ } else if (!this._vf.normalPerVertex) {
+
+ var indexes = this._vf.index;
+ this._mesh._normals[0] = [];
+
+ var faceCnt = 0;
+ for (i=0; i < indexes.length; ++i)
+ {
+ if (indexes[i] == -1) {
+ faceCnt++;
+ continue;
+ }
+
+ this._mesh._normals[0].push(pnts[faceCnt].x);
+ this._mesh._normals[0].push(pnts[faceCnt].y);
+ this._mesh._normals[0].push(pnts[faceCnt].z);
+ }
+ }
+
+ Array.forEach(this._parentNodes, function (node) {
+ node._dirty.normals = true;
+ });
+ }
+ else if (fieldName == "texCoord")
+ {
+ var texCoordNode = this._cf.texCoord.node;
+ if (x3dom.isa(texCoordNode, x3dom.nodeTypes.MultiTextureCoordinate)) {
+ if (texCoordNode._cf.texCoord.nodes.length)
+ texCoordNode = texCoordNode._cf.texCoord.nodes[0];
+ }
+ pnts = texCoordNode._vf.point;
+
+ this._mesh._texCoords[0] = pnts.toGL();
+
+ Array.forEach(this._parentNodes, function (node) {
+ node._dirty.texcoords = true;
+ });
+ }
+ }
+ }
+ }
+ )
+);
+/** @namespace x3dom.nodeTypes */
+/*
+ * X3DOM JavaScript Library
+ * http://www.x3dom.org
+ *
+ * (C)2009 Fraunhofer IGD, Darmstadt, Germany
+ * Dual licensed under the MIT and GPL
+ */
+
+/* ### X3DGeometricPropertyNode ### */
+x3dom.registerNodeType(
+ "X3DGeometricPropertyNode",
+ "Rendering",
+ defineClass(x3dom.nodeTypes.X3DNode,
+
+ /**
+ * Constructor for X3DGeometricPropertyNode
+ * @constructs x3dom.nodeTypes.X3DGeometricPropertyNode
+ * @x3d 3.3
+ * @component Rendering
+ * @status full
+ * @extends x3dom.nodeTypes.X3DNode
+ * @param {Object} [ctx=null] - context object, containing initial settings like namespace
+ * @classdesc This is the base node type for all geometric property node types defined in X3D.
+ */
+ function (ctx) {
+ x3dom.nodeTypes.X3DGeometricPropertyNode.superClass.call(this, ctx);
+
+ }
+ )
+);
+/** @namespace x3dom.nodeTypes */
+/*
+ * X3DOM JavaScript Library
+ * http://www.x3dom.org
+ *
+ * (C)2009 Fraunhofer IGD, Darmstadt, Germany
+ * Dual licensed under the MIT and GPL
+ */
+
+/* ### X3DCoordinateNode ### */
+x3dom.registerNodeType(
+ "X3DCoordinateNode",
+ "Rendering",
+ defineClass(x3dom.nodeTypes.X3DGeometricPropertyNode,
+
+ /**
+ * Constructor for X3DCoordinateNode
+ * @constructs x3dom.nodeTypes.X3DCoordinateNode
+ * @x3d 3.3
+ * @component Rendering
+ * @status full
+ * @extends x3dom.nodeTypes.X3DGeometricPropertyNode
+ * @param {Object} [ctx=null] - context object, containing initial settings like namespace
+ * @classdesc This is the base node type for all coordinate node types in X3D.
+ * All coordinates are specified in nodes derived from this abstract node type.
+ */
+ function (ctx) {
+ x3dom.nodeTypes.X3DCoordinateNode.superClass.call(this, ctx);
+
+ },
+ {
+ fieldChanged: function (fieldName) {
+ if (fieldName === "coord" || fieldName === "point") {
+ Array.forEach(this._parentNodes, function (node) {
+ node.fieldChanged("coord");
+ });
+ }
+ },
+
+ parentAdded: function (parent) {
+ if (parent._mesh && parent._cf.coord.node !== this) {
+ parent.fieldChanged("coord");
+ }
+ }
+ }
+ )
+);
+/** @namespace x3dom.nodeTypes */
+/*
+ * X3DOM JavaScript Library
+ * http://www.x3dom.org
+ *
+ * (C)2009 Fraunhofer IGD, Darmstadt, Germany
+ * Dual licensed under the MIT and GPL
+ */
+
+/* ### Coordinate ### */
+x3dom.registerNodeType(
+ "Coordinate",
+ "Rendering",
+ defineClass(x3dom.nodeTypes.X3DCoordinateNode,
+
+ /**
+ * Constructor for Coordinate
+ * @constructs x3dom.nodeTypes.Coordinate
+ * @x3d 3.3
+ * @component Rendering
+ * @status full
+ * @extends x3dom.nodeTypes.X3DCoordinateNode
+ * @param {Object} [ctx=null] - context object, containing initial settings like namespace
+ * @classdesc Coordinate builds geometry using a set of 3D coordinates.
+ * Coordinate is used by IndexedFaceSet, IndexedLineSet, LineSet and PointSet.
+ */
+ function (ctx) {
+ x3dom.nodeTypes.Coordinate.superClass.call(this, ctx);
+
+
+ /**
+ * Contains the 3D coordinates
+ * @var {x3dom.fields.MFVec3f} point
+ * @memberof x3dom.nodeTypes.Coordinate
+ * @initvalue []
+ * @field x3d
+ * @instance
+ */
+ this.addField_MFVec3f(ctx, 'point', []);
+
+ },
+ {
+ getPoints: function() {
+ return this._vf.point;
+ }
+ }
+ )
+);
+
+/** @namespace x3dom.nodeTypes */
+/*
+ * X3DOM JavaScript Library
+ * http://www.x3dom.org
+ *
+ * (C)2009 Fraunhofer IGD, Darmstadt, Germany
+ * Dual licensed under the MIT and GPL
+ */
+
+/* ### Normal ### */
+x3dom.registerNodeType(
+ "Normal",
+ "Rendering",
+ defineClass(x3dom.nodeTypes.X3DGeometricPropertyNode,
+
+ /**
+ * Constructor for Normal
+ * @constructs x3dom.nodeTypes.Normal
+ * @x3d 3.3
+ * @component Rendering
+ * @status full
+ * @extends x3dom.nodeTypes.X3DGeometricPropertyNode
+ * @param {Object} [ctx=null] - context object, containing initial settings like namespace
+ * @classdesc Normal is a set of 3D surface-normal vectors Normal values are optional perpendicular directions, used per-polygon or per-vertex for lighting and shading.
+ * Hint: used by IndexedFaceSet and ElevationGrid.
+ */
+ function (ctx) {
+ x3dom.nodeTypes.Normal.superClass.call(this, ctx);
+
+
+ /**
+ * set of unit-length normal vectors, corresponding to indexed polygons or vertices.
+ * @var {x3dom.fields.MFVec3f} vector
+ * @range [-1, 1]
+ * @memberof x3dom.nodeTypes.Normal
+ * @initvalue []
+ * @field x3dom
+ * @instance
+ */
+ this.addField_MFVec3f(ctx, 'vector', []);
+
+ },
+ {
+ fieldChanged: function (fieldName) {
+ if (fieldName === "normal" || fieldName === "vector") {
+ Array.forEach(this._parentNodes, function (node) {
+ node.fieldChanged("normal");
+ });
+ }
+ },
+
+ parentAdded: function (parent) {
+ if (parent._mesh && //parent._cf.coord.node &&
+ parent._cf.normal.node !== this) {
+ parent.fieldChanged("normal");
+ }
+ }
+ }
+ )
+);
+/** @namespace x3dom.nodeTypes */
+/*
+ * X3DOM JavaScript Library
+ * http://www.x3dom.org
+ *
+ * (C)2009 Fraunhofer IGD, Darmstadt, Germany
+ * Dual licensed under the MIT and GPL
+ */
+
+/* ### X3DColorNode ### */
+x3dom.registerNodeType(
+ "X3DColorNode",
+ "Rendering",
+ defineClass(x3dom.nodeTypes.X3DGeometricPropertyNode,
+
+ /**
+ * Constructor for X3DColorNode
+ * @constructs x3dom.nodeTypes.X3DColorNode
+ * @x3d 3.3
+ * @component Rendering
+ * @status full
+ * @extends x3dom.nodeTypes.X3DGeometricPropertyNode
+ * @param {Object} [ctx=null] - context object, containing initial settings like namespace
+ * @classdesc This is the base node type for color specifications in X3D.
+ */
+ function (ctx) {
+ x3dom.nodeTypes.X3DColorNode.superClass.call(this, ctx);
+
+ },
+ {
+ fieldChanged: function (fieldName) {
+ if (fieldName === "color") {
+ Array.forEach(this._parentNodes, function (node) {
+ node.fieldChanged("color");
+ });
+ }
+ },
+
+ parentAdded: function (parent) {
+ if (parent._mesh && //parent._cf.coord.node &&
+ parent._cf.color.node !== this) {
+ parent.fieldChanged("color");
+ }
+ }
+ }
+ )
+);
+/** @namespace x3dom.nodeTypes */
+/*
+ * X3DOM JavaScript Library
+ * http://www.x3dom.org
+ *
+ * (C)2009 Fraunhofer IGD, Darmstadt, Germany
+ * Dual licensed under the MIT and GPL
+ */
+
+/* ### Color ### */
+x3dom.registerNodeType(
+ "Color",
+ "Rendering",
+ defineClass(x3dom.nodeTypes.X3DColorNode,
+
+ /**
+ * Constructor for Color
+ * @constructs x3dom.nodeTypes.Color
+ * @x3d 3.3
+ * @component Rendering
+ * @status full
+ * @extends x3dom.nodeTypes.X3DColorNode
+ * @param {Object} [ctx=null] - context object, containing initial settings like namespace
+ * @classdesc This node defines a set of RGB colors to be used in the fields of another node.
+ * Color nodes are only used to specify multiple colours for a single geometric shape, such as colours for the faces or vertices of an IndexedFaceSet.
+ * A Material node is used to specify the overall material parameters of lit geometry.
+ * If both a Material node and a Color node are specified for a geometric shape, the colours shall replace the diffuse component of the material.
+ * RGB or RGBA textures take precedence over colours; specifying both an RGB or RGBA texture and a Color node for geometric shape will result in the Color node being ignored.
+ */
+ function (ctx) {
+ x3dom.nodeTypes.Color.superClass.call(this, ctx);
+
+
+ /**
+ * The RGB colors.
+ * @var {x3dom.fields.MFColor} color
+ * @range [0, 1]
+ * @memberof x3dom.nodeTypes.Color
+ * @initvalue []
+ * @field x3d
+ * @instance
+ */
+ this.addField_MFColor(ctx, 'color', []);
+
+ }
+ )
+);
+/** @namespace x3dom.nodeTypes */
+/*
+ * X3DOM JavaScript Library
+ * http://www.x3dom.org
+ *
+ * (C)2009 Fraunhofer IGD, Darmstadt, Germany
+ * Dual licensed under the MIT and GPL
+ */
+
+/* ### ColorRGBA ### */
+x3dom.registerNodeType(
+ "ColorRGBA",
+ "Rendering",
+ defineClass(x3dom.nodeTypes.X3DColorNode,
+
+ /**
+ * Constructor for ColorRGBA
+ * @constructs x3dom.nodeTypes.ColorRGBA
+ * @x3d 3.3
+ * @component Rendering
+ * @status full
+ * @extends x3dom.nodeTypes.X3DColorNode
+ * @param {Object} [ctx=null] - context object, containing initial settings like namespace
+ * @classdesc This node defines a set of RGBA colours to be used in the fields of another node.
+ * RGBA color nodes are only used to specify multiple colours with alpha for a single geometric shape, such as colours for the faces or vertices of an IndexedFaceSet.
+ * A Material node is used to specify the overall material parameters of lit geometry.
+ * If both a Material node and a ColorRGBA node are specified for a geometric shape, the colours shall replace the diffuse and transparency components of the material.
+ * RGB or RGBA textures take precedence over colours; specifying both an RGB or RGBA texture and a ColorRGBA node for geometric shape will result in the ColorRGBA node being ignored.
+ */
+ function (ctx) {
+ x3dom.nodeTypes.ColorRGBA.superClass.call(this, ctx);
+
+
+ /**
+ * The set of RGBA colors
+ * @var {x3dom.fields.MFColorRGBA} color
+ * @range [0, 1]
+ * @memberof x3dom.nodeTypes.ColorRGBA
+ * @initvalue []
+ * @field x3d
+ * @instance
+ */
+ this.addField_MFColorRGBA(ctx, 'color', []);
+
+ }
+ )
+);
+/** @namespace x3dom.nodeTypes */
+/*
+ * X3DOM JavaScript Library
+ * http://www.x3dom.org
+ *
+ * (C)2009 Fraunhofer IGD, Darmstadt, Germany
+ * Dual licensed under the MIT and GPL
+ */
+
+/* This is only a first stub */
+
+/* ### ParticleSet ### */
+x3dom.registerNodeType(
+ "ParticleSet",
+ "Rendering",
+ defineClass(x3dom.nodeTypes.PointSet,
+
+ /**
+ * Constructor for ParticleSet
+ * @constructs x3dom.nodeTypes.ParticleSet
+ * @x3d x.x
+ * @component Rendering
+ * @status experimental
+ * @extends x3dom.nodeTypes.PointSet
+ * @param {Object} [ctx=null] - context object, containing initial settings like namespace
+ * @classdesc The ParticleSet is a geometry node used in combination with a ParticleSystem node.
+ * Attention: So far this is only a stub.
+ */
+ function (ctx) {
+ x3dom.nodeTypes.ParticleSet.superClass.call(this, ctx);
+
+ /**
+ * Drawing mode: "ViewDirQuads" - Draws quads directed to the viewpoint (default). "Points" - Draw points.
+ * "Lines" - Draw lines. These modes must not match the finally supported modes.
+ * @var {x3dom.fields.SFString} mode
+ * @memberof x3dom.nodeTypes.ParticleSet
+ * @initvalue ViewDirQuads
+ * @range [ViewDirQuads, Points, Lines, Arrows, ViewerArrows, ViewerQuads, Rectangles]
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFString(ctx, 'mode', 'ViewDirQuads'); // only default value supported
+
+ /**
+ * Defines the drawing order for the particles. Possible values: "Any" - The order is undefined.
+ * "BackToFront" - Draw from back to front. "FrontToBack" - Draw from front to back.
+ * @var {x3dom.fields.SFString} drawOrder
+ * @memberof x3dom.nodeTypes.ParticleSet
+ * @initvalue Any
+ * @range [Any, BackToFront, FrontToBack]
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFString(ctx, 'drawOrder', 'Any');
+
+ // THINKABOUTME; does this very special field makes sense for being impl. in WebGL?
+ //this.addField_SFNode('secCoord', x3dom.nodeTypes.X3DCoordinateNode); // NOT YET SUPPORTED!
+
+ /**
+ * Stores a Normal node containing the normals of the particles.
+ * @var {x3dom.fields.SFNode} normal
+ * @memberof x3dom.nodeTypes.ParticleSet
+ * @initvalue null
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFNode('normal', x3dom.nodeTypes.Normal); // NOT YET SUPPORTED
+
+ /**
+ * An MFVec3f field containing the sizes of the particles.
+ * @var {x3dom.fields.MFVec3f} size
+ * @memberof x3dom.nodeTypes.ParticleSet
+ * @field x3dom
+ * @instance
+ */
+ this.addField_MFVec3f(ctx, 'size', []);
+
+ /**
+ * An MFInt32 field containing indices which specify the order of the vertices in the "coord" field.
+ * @var {x3dom.fields.MFInt32} index
+ * @memberof x3dom.nodeTypes.ParticleSet
+ * @field x3dom
+ * @instance
+ */
+ this.addField_MFInt32(ctx, 'index', []);
+
+ /**
+ * An MFFloat field containing z-values for the texture of a particle (used with 3D textures).
+ * @var {x3dom.fields.MFFloat} textureZ
+ * @memberof x3dom.nodeTypes.ParticleSet
+ * @field x3dom
+ * @instance
+ */
+ this.addField_MFFloat(ctx, 'textureZ', []); // NOT YET SUPPORTED! (3D textures not supported in WebGL)
+
+ this._mesh._primType = 'POINTS';
+ },
+ {
+ drawOrder: function() {
+ return this._vf.drawOrder.toLowerCase();
+ },
+
+ nodeChanged: function()
+ {
+ var coordNode = this._cf.coord.node;
+ x3dom.debug.assert(coordNode, "ParticleSet without coord node!");
+ var positions = coordNode.getPoints();
+
+ var numColComponents = 3;
+ var colorNode = this._cf.color.node;
+ var colors = new x3dom.fields.MFColor();
+ if (colorNode) {
+ colors = colorNode._vf.color;
+ x3dom.debug.assert(positions.length == colors.length, "Size of color and coord array differs!");
+
+ if (x3dom.isa(colorNode, x3dom.nodeTypes.ColorRGBA)) {
+ numColComponents = 4;
+ }
+ }
+
+ var normalNode = this._cf.normal.node;
+ var normals = new x3dom.fields.MFVec3f();
+ if (normalNode) {
+ normals = normalNode._vf.vector;
+ }
+
+ var indices = [];
+ if (this.drawOrder() != "any") {
+ indices = this._vf.index.toGL();
+
+ // generate indices since also used for sorting
+ if (indices.length == 0) {
+ var i, n = positions.length;
+ indices = new Array(n);
+ for (i = 0; i < n; i++) {
+ indices[i] = i;
+ }
+ }
+ }
+
+ this._mesh._numColComponents = numColComponents;
+ this._mesh._lit = false;
+
+ this._mesh._indices[0] = indices;
+ this._mesh._positions[0] = positions.toGL();
+ this._mesh._colors[0] = colors.toGL();
+ this._mesh._normals[0] = normals.toGL();
+ this._mesh._texCoords[0] = [];
+
+ this.invalidateVolume();
+ this._mesh._numCoords = this._mesh._positions[0].length / 3;
+ },
+
+ fieldChanged: function(fieldName)
+ {
+ var pnts = null;
+
+ if (fieldName == "index")
+ {
+ this._mesh._indices[0] = this._vf.index.toGL();
+
+ Array.forEach(this._parentNodes, function (node) {
+ node._dirty.indexes = true;
+ });
+ }
+ else if (fieldName == "size")
+ {
+ Array.forEach(this._parentNodes, function (node) {
+ node._dirty.specialAttribs = true;
+ });
+ }
+ else if (fieldName == "coord")
+ {
+ pnts = this._cf.coord.node.getPoints();
+
+ this._mesh._positions[0] = pnts.toGL();
+
+ var indices = [];
+ if (this.drawOrder() != "any") {
+ indices = this._vf.index.toGL();
+
+ // generate indices since also used for sorting
+ if (indices.length == 0) {
+ var i, n = pnts.length;
+ indices = new Array(n);
+ for (i = 0; i < n; i++) {
+ indices[i] = i;
+ }
+ }
+ }
+ this._mesh._indices[0] = indices;
+
+ this.invalidateVolume();
+
+ Array.forEach(this._parentNodes, function (node) {
+ node._dirty.positions = true;
+ node._dirty.indexes = true;
+ node.invalidateVolume();
+ });
+ }
+ else if (fieldName == "color")
+ {
+ pnts = this._cf.color.node._vf.color;
+
+ this._mesh._colors[0] = pnts.toGL();
+
+ Array.forEach(this._parentNodes, function (node) {
+ node._dirty.colors = true;
+ });
+ }
+ }
+ }
+ )
+);
+
+/** @namespace x3dom.nodeTypes */
+/*
+ * X3DOM JavaScript Library
+ * http://www.x3dom.org
+ *
+ * (C)2009 Fraunhofer IGD, Darmstadt, Germany
+ * Dual licensed under the MIT and GPL
+ */
+
+/* ### ClipPlane ### */
+x3dom.registerNodeType(
+ "ClipPlane",
+ "Rendering",
+ defineClass(x3dom.nodeTypes.X3DChildNode,
+
+ /**
+ * Constructor for ClipPlane
+ * @constructs x3dom.nodeTypes.ClipPlane
+ * @x3d 3.2
+ * @component Rendering
+ * @status full
+ * @extends x3dom.nodeTypes.X3DChildNode
+ * @param {Object} [ctx=null] - context object, containing initial settings like namespace
+ * @classdesc A clip plane is defined as a plane that generates two half-spaces. The effected geometry in the
+ * half-space that is defined as being outside the plane is removed from the rendered image as a result of a
+ * clipping operation.
+ */
+ function (ctx) {
+ x3dom.nodeTypes.ClipPlane.superClass.call(this, ctx);
+
+
+ /**
+ * Defines activation state of the clip plane.
+ * @var {x3dom.fields.SFBool} enabled
+ * @memberof x3dom.nodeTypes.ClipPlane
+ * @initvalue true
+ * @field x3d
+ * @instance
+ */
+ this.addField_SFBool(ctx, 'enabled', true);
+
+ /**
+ * The ClipPlane node specifies a single plane equation that will be used to clip the geometry.
+ * The plane field specifies a four-component plane equation that describes the inside and outside half
+ * space. The first three components are a normalized vector describing the direction of the plane's
+ * normal direction.
+ * @var {x3dom.fields.SFVec4f} plane
+ * @memberof x3dom.nodeTypes.ClipPlane
+ * @initvalue 0,1,0,0
+ * @field x3d
+ * @instance
+ */
+ this.addField_SFVec4f(ctx, 'plane', 0, 1, 0, 0);
+
+ /**
+ * Defines the strength of the capping.
+ * @var {x3dom.fields.SFFloat} cappingStrength
+ * @memberof x3dom.nodeTypes.ClipPlane
+ * @initvalue 0.0
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFFloat(ctx, 'cappingStrength', 0.0);
+
+ /**
+ * Defines the color of the capping.
+ * @var {x3dom.fields.SFColor} cappingColor
+ * @memberof x3dom.nodeTypes.ClipPlane
+ * @initvalue 1.0,1.0,1.0
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFColor(ctx, 'cappingColor', 1.0, 1.0, 1.0);
+
+
+ /**
+ * Enables/disables this effector (e.g. light)
+ * @var {x3dom.fields.SFBool} on
+ * @memberof x3dom.nodeTypes.ClipPlane
+ * @initvalue true
+ * @field x3d
+ * @instance
+ */
+ this.addField_SFBool(ctx, 'on', true);
+ },
+ {
+ fieldChanged: function (fieldName) {
+ if (fieldName == "enabled" || fieldName == "on") {
+ //TODO
+ }
+ },
+
+ nodeChanged: function () {
+ x3dom.nodeTypes.ClipPlane.count++;
+ },
+
+ onRemove: function() {
+ x3dom.nodeTypes.ClipPlane.count--;
+ },
+
+ parentAdded: function(parent) {
+ },
+
+ parentRemoved: function(parent) {
+ //TODO
+ }
+ }
+ )
+);
+
+/** Static class ID counter (needed for caching) */
+x3dom.nodeTypes.ClipPlane.count = 0;
+
+/** @namespace x3dom.nodeTypes */
+/*
+ * X3DOM JavaScript Library
+ * http://www.x3dom.org
+ *
+ * (C)2009 Fraunhofer IGD, Darmstadt, Germany
+ * Dual licensed under the MIT and GPL
+ */
+
+/* ### X3DAppearanceNode ### */
+x3dom.registerNodeType(
+ "X3DAppearanceNode",
+ "Shape",
+ defineClass(x3dom.nodeTypes.X3DNode,
+
+ /**
+ * Constructor for X3DAppearanceNode
+ * @constructs x3dom.nodeTypes.X3DAppearanceNode
+ * @x3d 3.3
+ * @component Shape
+ * @status full
+ * @extends x3dom.nodeTypes.X3DNode
+ * @param {Object} [ctx=null] - context object, containing initial settings like namespace
+ * @classdesc This is the base node type for all Appearance nodes.
+ */
+ function (ctx) {
+ x3dom.nodeTypes.X3DAppearanceNode.superClass.call(this, ctx);
+
+ }
+ )
+);
+/** @namespace x3dom.nodeTypes */
+/*
+ * X3DOM JavaScript Library
+ * http://www.x3dom.org
+ *
+ * (C)2009 Fraunhofer IGD, Darmstadt, Germany
+ * Dual licensed under the MIT and GPL
+ */
+
+/* ### Appearance ### */
+x3dom.registerNodeType(
+ "Appearance",
+ "Shape",
+ defineClass(x3dom.nodeTypes.X3DAppearanceNode,
+
+ /**
+ * Constructor for Appearance
+ * @constructs x3dom.nodeTypes.Appearance
+ * @x3d 3.3
+ * @component Shape
+ * @status experimental
+ * @extends x3dom.nodeTypes.X3DAppearanceNode
+ * @param {Object} [ctx=null] - context object, containing initial settings like namespace
+ * @classdesc The Appearance node specifies the visual properties of geometry.
+ * The value for each of the fields in this node may be NULL.
+ * However, if the field is non-NULL, it shall contain one node of the appropriate type.
+ */
+ function (ctx) {
+ x3dom.nodeTypes.Appearance.superClass.call(this, ctx);
+
+
+ /**
+ * The material field, if specified, shall contain a Material node.
+ * If the material field is NULL or unspecified, lighting is off (all lights are ignored during rendering of the object that references this Appearance) and the unlit object colour is (1, 1, 1).
+ * @var {x3dom.fields.SFNode} material
+ * @memberof x3dom.nodeTypes.Appearance
+ * @initvalue x3dom.nodeTypes.X3DMaterialNode
+ * @field x3d
+ * @instance
+ */
+ this.addField_SFNode('material', x3dom.nodeTypes.X3DMaterialNode);
+
+ /**
+ * The texture field, if specified, shall contain a texture nodes.
+ * If the texture node is NULL or the texture field is unspecified, the object that references this Appearance is not textured.
+ * @var {x3dom.fields.SFNode} texture
+ * @memberof x3dom.nodeTypes.Appearance
+ * @initvalue x3dom.nodeTypes.X3DTextureNode
+ * @field x3d
+ * @instance
+ */
+ this.addField_SFNode('texture', x3dom.nodeTypes.X3DTextureNode);
+
+ /**
+ * The textureTransform field, if specified, shall contain a TextureTransform node. If the textureTransform is NULL or unspecified, the textureTransform field has no effect.
+ * @var {x3dom.fields.SFNode} textureTransform
+ * @memberof x3dom.nodeTypes.Appearance
+ * @initvalue x3dom.nodeTypes.X3DTextureTransformNode
+ * @field x3d
+ * @instance
+ */
+ this.addField_SFNode('textureTransform', x3dom.nodeTypes.X3DTextureTransformNode);
+
+ /**
+ * The lineProperties field, if specified, shall contain a LineProperties node. If lineProperties is NULL or unspecified, the lineProperties field has no effect.
+ * @var {x3dom.fields.SFNode} lineProperties
+ * @memberof x3dom.nodeTypes.Appearance
+ * @initvalue x3dom.nodeTypes.LineProperties
+ * @field x3d
+ * @instance
+ */
+ this.addField_SFNode('lineProperties', x3dom.nodeTypes.LineProperties);
+
+ /**
+ * Holds a ColorMaskMode node.
+ * @var {x3dom.fields.SFNode} colorMaskMode
+ * @memberof x3dom.nodeTypes.Appearance
+ * @initvalue x3dom.nodeTypes.ColorMaskMode
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFNode('colorMaskMode', x3dom.nodeTypes.ColorMaskMode);
+
+ /**
+ * Holds the BlendMode node, that is needed for correct transparency.
+ * @var {x3dom.fields.SFNode} blendMode
+ * @memberof x3dom.nodeTypes.Appearance
+ * @initvalue x3dom.nodeTypes.BlendMode
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFNode('blendMode', x3dom.nodeTypes.BlendMode);
+
+ /**
+ * Holds the depthMode node.
+ * @var {x3dom.fields.SFNode} depthMode
+ * @memberof x3dom.nodeTypes.Appearance
+ * @initvalue x3dom.nodeTypes.DepthMode
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFNode('depthMode', x3dom.nodeTypes.DepthMode);
+
+ /**
+ * Contains ProgramShader (Cg) or ComposedShader (GLSL).
+ * @var {x3dom.fields.MFNode} shaders
+ * @memberof x3dom.nodeTypes.Appearance
+ * @initvalue x3dom.nodeTypes.X3DShaderNode
+ * @field x3dom
+ * @instance
+ */
+ this.addField_MFNode('shaders', x3dom.nodeTypes.X3DShaderNode);
+
+ /**
+ * Defines the shape type for sorting.
+ * @var {x3dom.fields.SFString} sortType
+ * @range [auto, transparent, opaque]
+ * @memberof x3dom.nodeTypes.Appearance
+ * @initvalue 'auto'
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFString(ctx, 'sortType', 'auto');
+
+ /**
+ * Change render order manually.
+ * @var {x3dom.fields.SFInt32} sortKey
+ * @memberof x3dom.nodeTypes.Appearance
+ * @initvalue 0
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFInt32(ctx, 'sortKey', 0);
+
+ // shortcut to shader program
+ this._shader = null;
+
+ },
+ {
+ nodeChanged: function() {
+ //TODO delete this if all works fine
+ if (!this._cf.material.node) {
+ //Unlit
+ //this.addChild(x3dom.nodeTypes.Material.defaultNode());
+ }
+
+ if (this._cf.shaders.nodes.length) {
+ this._shader = this._cf.shaders.nodes[0];
+ }
+ else if(this._shader)
+ this._shader=null;
+
+ Array.forEach(this._parentNodes, function (shape) {
+ shape.setAppDirty();
+ });
+
+ this.checkSortType();
+ },
+
+ checkSortType: function() {
+ if (this._vf.sortType == 'auto') {
+ if (this._cf.material.node && (this._cf.material.node._vf.transparency > 0 ||
+ this._cf.material.node._vf.backTransparency && this._cf.material.node._vf.backTransparency > 0)) {
+ this._vf.sortType = 'transparent';
+ }
+ else if (this._cf.texture.node && this._cf.texture.node._vf.url.length) {
+ // uhh, this is a rather coarse guess...
+ if (this._cf.texture.node._vf.url[0].toLowerCase().indexOf('.'+'png') >= 0) {
+ this._vf.sortType = 'transparent';
+ }
+ else {
+ this._vf.sortType = 'opaque';
+ }
+ }
+ else {
+ this._vf.sortType = 'opaque';
+ }
+ }
+ },
+
+ texTransformMatrix: function() {
+ if (this._cf.textureTransform.node === null) {
+ return x3dom.fields.SFMatrix4f.identity();
+ }
+ else {
+ return this._cf.textureTransform.node.texTransformMatrix();
+ }
+ },
+
+ parentAdded: function(parent) {
+ if (this != x3dom.nodeTypes.Appearance._defaultNode) {
+ /*if (parent._cleanupGLObjects) {
+ parent._cleanupGLObjects(true);
+ }*/
+ parent.setAppDirty();
+ }
+ }
+ }
+ )
+);
+
+x3dom.nodeTypes.Appearance.defaultNode = function() {
+ if (!x3dom.nodeTypes.Appearance._defaultNode) {
+ x3dom.nodeTypes.Appearance._defaultNode = new x3dom.nodeTypes.Appearance();
+ x3dom.nodeTypes.Appearance._defaultNode.nodeChanged();
+ }
+ return x3dom.nodeTypes.Appearance._defaultNode;
+};
+/** @namespace x3dom.nodeTypes */
+/*
+ * X3DOM JavaScript Library
+ * http://www.x3dom.org
+ *
+ * (C)2009 Fraunhofer IGD, Darmstadt, Germany
+ * Dual licensed under the MIT and GPL
+ */
+
+/* ### X3DAppearanceChildNode ### */
+x3dom.registerNodeType(
+ "X3DAppearanceChildNode",
+ "Shape",
+ defineClass(x3dom.nodeTypes.X3DNode,
+
+ /**
+ * Constructor for X3DAppearanceChildNode
+ * @constructs x3dom.nodeTypes.X3DAppearanceChildNode
+ * @x3d 3.3
+ * @component Shape
+ * @status full
+ * @extends x3dom.nodeTypes.X3DNode
+ * @param {Object} [ctx=null] - context object, containing initial settings like namespace
+ * @classdesc This is the base node type for the child nodes of the X3DAppearanceNode type.
+ */
+ function (ctx) {
+ x3dom.nodeTypes.X3DAppearanceChildNode.superClass.call(this, ctx);
+
+ }
+ )
+);
+/** @namespace x3dom.nodeTypes */
+/*
+ * X3DOM JavaScript Library
+ * http://www.x3dom.org
+ *
+ * (C)2009 Fraunhofer IGD, Darmstadt, Germany
+ * Dual licensed under the MIT and GPL
+ */
+
+/* ### BlendMode ### */
+x3dom.registerNodeType(
+ "BlendMode",
+ "Shape",
+ defineClass(x3dom.nodeTypes.X3DAppearanceChildNode,
+
+ /**
+ * Constructor for BlendMode
+ * @constructs x3dom.nodeTypes.BlendMode
+ * @x3d x.x
+ * @component Shape
+ * @extends x3dom.nodeTypes.X3DAppearanceChildNode
+ * @param {Object} [ctx=null] - context object, containing initial settings like namespace
+ * @classdesc The BlendMode controls blending and alpha test.
+ * Pixels can be drawn using a function that blends the incoming (source) RGBA values with the RGBA values that are already in the frame buffer (the destination values).
+ */
+ function (ctx) {
+ x3dom.nodeTypes.BlendMode.superClass.call(this, ctx);
+
+
+ /**
+ * The incoming pixel is scaled according to the method defined by the source factor.
+ * @var {x3dom.fields.SFString} srcFactor
+ * @range [none, zero, one, dst_color, src_color, one_minus_dst_color, one_minus_src_color, src_alpha, one_minus_src_alpha, dst_alpha, one_minus_dst_alpha, src_alpha_saturate, constant_color, one_minus_constant_color, constant_alpha, one_minus_constant_alpha]
+ * @memberof x3dom.nodeTypes.BlendMode
+ * @initvalue "src_alpha"
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFString(ctx, 'srcFactor', "src_alpha");
+
+ /**
+ * The frame buffer pixel is scaled according to the method defined by the destination factor.
+ * @var {x3dom.fields.SFString} destFactor
+ * @range [none, zero, one, dst_color, src_color, one_minus_dst_color, one_minus_src_color, src_alpha, one_minus_src_alpha, dst_alpha, one_minus_dst_alpha, src_alpha_saturate, constant_color, one_minus_constant_color, constant_alpha, one_minus_constant_alpha]
+ * @memberof x3dom.nodeTypes.BlendMode
+ * @initvalue "one_minus_src_alpha"
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFString(ctx, 'destFactor', "one_minus_src_alpha");
+
+ /**
+ * This is the constant color used by blend modes constant.
+ * @var {x3dom.fields.SFColor} color
+ * @memberof x3dom.nodeTypes.BlendMode
+ * @initvalue 1,1,1
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFColor(ctx, 'color', 1, 1, 1);
+
+ /**
+ * This is the constant alpha used by blend modes constant.
+ * @var {x3dom.fields.SFFloat} colorTransparency
+ * @memberof x3dom.nodeTypes.BlendMode
+ * @initvalue 0
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFFloat(ctx, 'colorTransparency', 0);
+
+ /**
+ *
+ * @var {x3dom.fields.SFString} alphaFunc
+ * @memberof x3dom.nodeTypes.BlendMode
+ * @initvalue "none"
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFString(ctx, 'alphaFunc', "none");
+
+ /**
+ * The alphaFunc defines how fragments which do not fulfill a certain condition are handled.
+ * @var {x3dom.fields.SFFloat} alphaFuncValue
+ * @range [none, never, less, equal, lequal, greater, notequal, gequal, always]
+ * @memberof x3dom.nodeTypes.BlendMode
+ * @initvalue 0
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFFloat(ctx, 'alphaFuncValue', 0);
+
+ /**
+ * An additional equation used to combine source, destination and the constant value.
+ * @var {x3dom.fields.SFString} equation
+ * @range [none, func_add, func_subtract, func_reverse_subtract, min, max, logic_op]
+ * @memberof x3dom.nodeTypes.BlendMode
+ * @initvalue "none"
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFString(ctx, 'equation', "none");
+
+ }
+ )
+);
+/** @namespace x3dom.nodeTypes */
+/*
+ * X3DOM JavaScript Library
+ * http://www.x3dom.org
+ *
+ * (C)2009 Fraunhofer IGD, Darmstadt, Germany
+ * Dual licensed under the MIT and GPL
+ */
+
+/* ### DepthMode ### */
+x3dom.registerNodeType(
+ "DepthMode",
+ "Shape",
+ defineClass(x3dom.nodeTypes.X3DAppearanceChildNode,
+
+ /**
+ * Constructor for DepthMode
+ * @constructs x3dom.nodeTypes.DepthMode
+ * @x3d x.x
+ * @component Shape
+ * @extends x3dom.nodeTypes.X3DAppearanceChildNode
+ * @param {Object} [ctx=null] - context object, containing initial settings like namespace
+ * @classdesc The depth mode contains the parameters that are specific for depth control, like the value used for depth buffer comparisons.
+ */
+ function (ctx) {
+ x3dom.nodeTypes.DepthMode.superClass.call(this, ctx);
+
+
+ /**
+ * Whether the depth test should be enabled or not.
+ * @var {x3dom.fields.SFBool} enableDepthTest
+ * @memberof x3dom.nodeTypes.DepthMode
+ * @initvalue true
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFBool(ctx, 'enableDepthTest', true);
+
+ /**
+ * The depth function to use. If "none", it's not changed, the default is "lequal".
+ * @var {x3dom.fields.SFString} depthFunc
+ * @range [NONE, NEVER, LESS, EQUAL, LEQUAL, GREATER, NOTEQUAL, GEQUAL, ALWAYS]
+ * @memberof x3dom.nodeTypes.DepthMode
+ * @initvalue "none"
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFString(ctx, 'depthFunc', "none");
+
+ /**
+ * Whether the depth buffer is enabled for writing or not.
+ * @var {x3dom.fields.SFBool} readOnly
+ * @memberof x3dom.nodeTypes.DepthMode
+ * @initvalue false
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFBool(ctx, 'readOnly', false);
+
+ /**
+ * The near value for the depth range. Ignored if less than 0, defaults to -1.
+ * @var {x3dom.fields.SFFloat} zNearRange
+ * @range [0, 1]
+ * @memberof x3dom.nodeTypes.DepthMode
+ * @initvalue -1
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFFloat(ctx, 'zNearRange', -1);
+
+ /**
+ * The far value for the depth range. Ignored if less than 0, defaults to -1.
+ * @var {x3dom.fields.SFFloat} zFarRange
+ * @range [0, 1]
+ * @memberof x3dom.nodeTypes.DepthMode
+ * @initvalue -1
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFFloat(ctx, 'zFarRange', -1);
+
+ }
+ )
+);
+/** @namespace x3dom.nodeTypes */
+/*
+ * X3DOM JavaScript Library
+ * http://www.x3dom.org
+ *
+ * (C)2009 Fraunhofer IGD, Darmstadt, Germany
+ * Dual licensed under the MIT and GPL
+ */
+
+/* ### ColorMaskMode ### */
+x3dom.registerNodeType(
+ "ColorMaskMode",
+ "Shape",
+ defineClass(x3dom.nodeTypes.X3DAppearanceChildNode,
+
+ /**
+ * Constructor for ColorMaskMode
+ * @constructs x3dom.nodeTypes.ColorMaskMode
+ * @x3d x.x
+ * @component Shape
+ * @extends x3dom.nodeTypes.X3DAppearanceChildNode
+ * @param {Object} [ctx=null] - context object, containing initial settings like namespace
+ * @classdesc The ColorMaskMode node affects drawing in RGBA mode. The 4 masks control whether the corresponding component is written.
+ */
+ function (ctx) {
+ x3dom.nodeTypes.ColorMaskMode.superClass.call(this, ctx);
+
+
+ /**
+ * Masks r color channel.
+ * @var {x3dom.fields.SFBool} maskR
+ * @memberof x3dom.nodeTypes.ColorMaskMode
+ * @initvalue true
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFBool(ctx, 'maskR', true);
+
+ /**
+ * Masks g color channel.
+ * @var {x3dom.fields.SFBool} maskG
+ * @memberof x3dom.nodeTypes.ColorMaskMode
+ * @initvalue true
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFBool(ctx, 'maskG', true);
+
+ /**
+ * Masks b color channel.
+ * @var {x3dom.fields.SFBool} maskB
+ * @memberof x3dom.nodeTypes.ColorMaskMode
+ * @initvalue true
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFBool(ctx, 'maskB', true);
+
+ /**
+ * Masks a color channel.
+ * @var {x3dom.fields.SFBool} maskA
+ * @memberof x3dom.nodeTypes.ColorMaskMode
+ * @initvalue true
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFBool(ctx, 'maskA', true);
+
+ }
+ )
+);
+/** @namespace x3dom.nodeTypes */
+/*
+ * X3DOM JavaScript Library
+ * http://www.x3dom.org
+ *
+ * (C)2009 Fraunhofer IGD, Darmstadt, Germany
+ * Dual licensed under the MIT and GPL
+ */
+
+/* ### LineProperties ### */
+x3dom.registerNodeType(
+ "LineProperties",
+ "Shape",
+ defineClass(x3dom.nodeTypes.X3DAppearanceChildNode,
+
+ /**
+ * Constructor for LineProperties
+ * @constructs x3dom.nodeTypes.LineProperties
+ * @x3d 3.3
+ * @component Shape
+ * @status experimental
+ * @extends x3dom.nodeTypes.X3DAppearanceChildNode
+ * @param {Object} [ctx=null] - context object, containing initial settings like namespace
+ * @classdesc The LineProperties node specifies additional properties to be applied to all line geometry. The colour of the line is specified by the associated Material node.
+ */
+ function (ctx) {
+ x3dom.nodeTypes.LineProperties.superClass.call(this, ctx);
+
+ // http://www.web3d.org/files/specifications/19775-1/V3.2/Part01/components/shape.html#LineProperties
+ // THINKABOUTME: to my mind, the only useful, but missing, field is linewidth (scaleFactor is overhead)
+
+ /**
+ * The linetype and linewidth shall only be applied when the applied field has value TRUE.
+ * When the value of the applied field is FALSE, a solid line of nominal width shall be produced.
+ * @var {x3dom.fields.SFBool} applied
+ * @memberof x3dom.nodeTypes.LineProperties
+ * @initvalue true
+ * @field x3d
+ * @instance
+ */
+ this.addField_SFBool(ctx, 'applied', true);
+
+ /**
+ * The linetype field selects a line pattern.
+ * @var {x3dom.fields.SFInt32} linetype
+ * @range [0, inf]
+ * @memberof x3dom.nodeTypes.LineProperties
+ * @initvalue 1
+ * @field x3d
+ * @instance
+ */
+ this.addField_SFInt32(ctx, 'linetype', 1);
+
+ /**
+ * The linewidthScaleFactor is a multiplicative value that scales a the linewidth. This resulting value shall then be mapped to the nearest available line width. A value less than or equal to zero refers to the minimum available line width.
+ * @var {x3dom.fields.SFFloat} linewidthScaleFactor
+ * @range [0, inf]
+ * @memberof x3dom.nodeTypes.LineProperties
+ * @initvalue 0
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFFloat(ctx, 'linewidthScaleFactor', 0);
+
+ }
+ )
+);
+/** @namespace x3dom.nodeTypes */
+/*
+ * X3DOM JavaScript Library
+ * http://www.x3dom.org
+ *
+ * (C)2009 Fraunhofer IGD, Darmstadt, Germany
+ * Dual licensed under the MIT and GPL
+ */
+
+/* ### X3DMaterialNode ### */
+x3dom.registerNodeType(
+ "X3DMaterialNode",
+ "Shape",
+ defineClass(x3dom.nodeTypes.X3DAppearanceChildNode,
+
+ /**
+ * Constructor for X3DMaterialNode
+ * @constructs x3dom.nodeTypes.X3DMaterialNode
+ * @x3d 3.3
+ * @component Shape
+ * @status full
+ * @extends x3dom.nodeTypes.X3DAppearanceChildNode
+ * @param {Object} [ctx=null] - context object, containing initial settings like namespace
+ * @classdesc This is the base node type for all Material nodes.
+ */
+ function (ctx) {
+ x3dom.nodeTypes.X3DMaterialNode.superClass.call(this, ctx);
+
+ }
+ )
+);
+/** @namespace x3dom.nodeTypes */
+/*
+ * X3DOM JavaScript Library
+ * http://www.x3dom.org
+ *
+ * (C)2009 Fraunhofer IGD, Darmstadt, Germany
+ * Dual licensed under the MIT and GPL
+ */
+
+/* ### Material ### */
+x3dom.registerNodeType(
+ "Material",
+ "Shape",
+ defineClass(x3dom.nodeTypes.X3DMaterialNode,
+
+ /**
+ * Constructor for Material
+ * @constructs x3dom.nodeTypes.Material
+ * @x3d 3.3
+ * @component Shape
+ * @status full
+ * @extends x3dom.nodeTypes.X3DMaterialNode
+ * @param {Object} [ctx=null] - context object, containing initial settings like namespace
+ * @classdesc The Material node specifies surface material properties for associated geometry nodes and is used by the X3D lighting equations during rendering.
+ * All of the fields in the Material node range from 0.0 to 1.0.
+ */
+ function (ctx) {
+ x3dom.nodeTypes.Material.superClass.call(this, ctx);
+
+ /**
+ * The ambientIntensity field specifies how much ambient light from light sources this surface shall reflect.
+ * Ambient light is omnidirectional and depends only on the number of light sources, not their positions with respect to the surface.
+ * Ambient colour is calculated as ambientIntensity × diffuseColor.
+ * @var {x3dom.fields.SFFloat} ambientIntensity
+ * @range [0, 1]
+ * @memberof x3dom.nodeTypes.X3DMaterialNode
+ * @initvalue 0.2
+ * @field x3d
+ * @instance
+ */
+ this.addField_SFFloat(ctx, 'ambientIntensity', 0.2);
+
+ /**
+ * The diffuseColor field reflects all X3D light sources depending on the angle of the surface with respect to the light source.
+ * The more directly the surface faces the light, the more diffuse light reflects.
+ * The emissiveColor field models "glowing" objects.
+ * This can be useful for displaying pre-lit models (where the light energy of the room is computed explicitly), or for displaying scientific data.
+ * @var {x3dom.fields.SFColor} diffuseColor
+ * @memberof x3dom.nodeTypes.X3DMaterialNode
+ * @initvalue 0.8,0.8,0.8
+ * @field x3d
+ * @instance
+ */
+ this.addField_SFColor(ctx, 'diffuseColor', 0.8, 0.8, 0.8);
+
+ /**
+ * The emissiveColor field models "glowing" objects.
+ * This can be useful for displaying pre-lit models (where the light energy of the room is computed explicitly), or for displaying scientific data.
+ * @var {x3dom.fields.SFColor} emissiveColor
+ * @memberof x3dom.nodeTypes.X3DMaterialNode
+ * @initvalue 0,0,0
+ * @field x3d
+ * @instance
+ */
+ this.addField_SFColor(ctx, 'emissiveColor', 0, 0, 0);
+
+ /**
+ * The specularColor and shininess fields determine the specular highlights (e.g., the shiny spots on an apple).
+ * When the angle from the light to the surface is close to the angle from the surface to the viewer, the specularColor is added to the diffuse and ambient colour calculations.
+ * Lower shininess values produce soft glows, while higher values result in sharper, smaller highlights.
+ * @var {x3dom.fields.SFFloat} shininess
+ * @range [0, 1]
+ * @memberof x3dom.nodeTypes.X3DMaterialNode
+ * @initvalue 0.2
+ * @field x3d
+ * @instance
+ */
+ this.addField_SFFloat(ctx, 'shininess', 0.2);
+
+ /**
+ * The specularColor and shininess fields determine the specular highlights (e.g., the shiny spots on an apple).
+ * When the angle from the light to the surface is close to the angle from the surface to the viewer, the specularColor is added to the diffuse and ambient colour calculations.
+ * Lower shininess values produce soft glows, while higher values result in sharper, smaller highlights.
+ * @var {x3dom.fields.SFColor} specularColor
+ * @memberof x3dom.nodeTypes.X3DMaterialNode
+ * @initvalue 0,0,0
+ * @field x3d
+ * @instance
+ */
+ this.addField_SFColor(ctx, 'specularColor', 0, 0, 0);
+
+ /**
+ * The transparency field specifies how "clear" an object is, with 1.0 being completely transparent, and 0.0 completely opaque.
+ * @var {x3dom.fields.SFFloat} transparency
+ * @range [0, 1]
+ * @memberof x3dom.nodeTypes.X3DMaterialNode
+ * @initvalue 0
+ * @field x3d
+ * @instance
+ */
+ this.addField_SFFloat(ctx, 'transparency', 0);
+
+ },
+ {
+ fieldChanged: function(fieldName) {
+ if (fieldName == "ambientIntensity" || fieldName == "diffuseColor" ||
+ fieldName == "emissiveColor" || fieldName == "shininess" ||
+ fieldName == "specularColor" || fieldName == "transparency")
+ {
+ Array.forEach(this._parentNodes, function (app) {
+ Array.forEach(app._parentNodes, function (shape) {
+ shape._dirty.material = true;
+ });
+ app.checkSortType();
+ });
+ }
+ }
+ }
+ )
+);
+
+x3dom.nodeTypes.Material.defaultNode = function() {
+ if (!x3dom.nodeTypes.Material._defaultNode) {
+ x3dom.nodeTypes.Material._defaultNode = new x3dom.nodeTypes.Material();
+ x3dom.nodeTypes.Material._defaultNode.nodeChanged();
+ }
+ return x3dom.nodeTypes.Material._defaultNode;
+};
+/** @namespace x3dom.nodeTypes */
+/*
+ * X3DOM JavaScript Library
+ * http://www.x3dom.org
+ *
+ * (C)2009 Fraunhofer IGD, Darmstadt, Germany
+ * Dual licensed under the MIT and GPL
+ */
+
+/* ### TwoSidedMaterial ### */
+x3dom.registerNodeType(
+ "TwoSidedMaterial",
+ "Shape",
+ defineClass(x3dom.nodeTypes.Material,
+
+ /**
+ * Constructor for TwoSidedMaterial
+ * @constructs x3dom.nodeTypes.TwoSidedMaterial
+ * @x3d 3.3
+ * @component Shape
+ * @status full
+ * @extends x3dom.nodeTypes.X3DMaterialNode
+ * @param {Object} [ctx=null] - context object, containing initial settings like namespace
+ * @classdesc This node defines material properties that can effect both the front and back side of a polygon individually.
+ * These materials are used for both the front and back side of the geometry whenever the X3D lighting model is active.
+ */
+ function (ctx) {
+ x3dom.nodeTypes.TwoSidedMaterial.superClass.call(this, ctx);
+
+
+ /**
+ * Defines the ambient intensity for the back side.
+ * @var {x3dom.fields.SFFloat} backAmbientIntensity
+ * @range [0, 1]
+ * @memberof x3dom.nodeTypes.TwoSidedMaterial
+ * @initvalue 0.2
+ * @field x3d
+ * @instance
+ */
+ this.addField_SFFloat(ctx, 'backAmbientIntensity', 0.2);
+
+ /**
+ * Defines the diffuse color for the back side.
+ * @var {x3dom.fields.SFColor} backDiffuseColor
+ * @memberof x3dom.nodeTypes.TwoSidedMaterial
+ * @initvalue 0.8,0.8,0.8
+ * @field x3d
+ * @instance
+ */
+ this.addField_SFColor(ctx, 'backDiffuseColor', 0.8, 0.8, 0.8);
+
+ /**
+ * Defines the emissive color for the back side.
+ * @var {x3dom.fields.SFColor} backEmissiveColor
+ * @memberof x3dom.nodeTypes.TwoSidedMaterial
+ * @initvalue 0,0,0
+ * @field x3d
+ * @instance
+ */
+ this.addField_SFColor(ctx, 'backEmissiveColor', 0, 0, 0);
+
+ /**
+ * Defines the shininess for the back side.
+ * @var {x3dom.fields.SFFloat} backShininess
+ * @range [0, 1]
+ * @memberof x3dom.nodeTypes.TwoSidedMaterial
+ * @initvalue 0.2
+ * @field x3d
+ * @instance
+ */
+ this.addField_SFFloat(ctx, 'backShininess', 0.2);
+
+ /**
+ * Defines the specular color for the back side.
+ * @var {x3dom.fields.SFColor} backSpecularColor
+ * @memberof x3dom.nodeTypes.TwoSidedMaterial
+ * @initvalue 0,0,0
+ * @field x3d
+ * @instance
+ */
+ this.addField_SFColor(ctx, 'backSpecularColor', 0, 0, 0);
+
+ /**
+ * Defines the transparency for the back side.
+ * @var {x3dom.fields.SFFloat} backTransparency
+ * @range [0, 1]
+ * @memberof x3dom.nodeTypes.TwoSidedMaterial
+ * @initvalue 0
+ * @field x3d
+ * @instance
+ */
+ this.addField_SFFloat(ctx, 'backTransparency', 0);
+
+ /**
+ * If the separateBackColor field is set to TRUE, the rendering shall render the front and back faces of the geometry with different values.
+ * If the value is FALSE, the front colours are used for both the front and back side of the polygon, as per the existing X3D lighting rules.
+ * @var {x3dom.fields.SFBool} separateBackColor
+ * @memberof x3dom.nodeTypes.TwoSidedMaterial
+ * @initvalue false
+ * @field x3d
+ * @instance
+ */
+ this.addField_SFBool(ctx, 'separateBackColor', false);
+
+ },
+ {
+ fieldChanged: function(fieldName) {
+ if (fieldName == "ambientIntensity" || fieldName == "diffuseColor" ||
+ fieldName == "emissiveColor" || fieldName == "shininess" ||
+ fieldName == "specularColor" || fieldName == "transparency" ||
+ fieldName == "backAmbientIntensity" || fieldName == "backDiffuseColor" ||
+ fieldName == "backEmissiveColor" || fieldName == "backShininess" ||
+ fieldName == "backSpecularColor" || fieldName == "backTransparency" ||
+ fieldName == "separateBackColor")
+ {
+ Array.forEach(this._parentNodes, function (app) {
+ Array.forEach(app._parentNodes, function (shape) {
+ shape._dirty.material = true;
+ });
+ app.checkSortType();
+ });
+ }
+ }
+ }
+ )
+);
+/** @namespace x3dom.nodeTypes */
+/*
+ * X3DOM JavaScript Library
+ * http://www.x3dom.org
+ *
+ * (C)2009 Fraunhofer IGD, Darmstadt, Germany
+ * Dual licensed under the MIT and GPL
+ */
+
+/* ### X3DShapeNode ### */
+x3dom.registerNodeType(
+ "X3DShapeNode",
+ "Shape",
+ defineClass(x3dom.nodeTypes.X3DBoundedObject,
+
+ /**
+ * Constructor for X3DShapeNode
+ * @constructs x3dom.nodeTypes.X3DShapeNode
+ * @x3d 3.3
+ * @component Shape
+ * @status full
+ * @extends x3dom.nodeTypes.X3DBoundedObject
+ * @param {Object} [ctx=null] - context object, containing initial settings like namespace
+ * @classdesc This is the base node type for all Shape nodes.
+ */
+ function (ctx) {
+ x3dom.nodeTypes.X3DShapeNode.superClass.call(this, ctx);
+
+
+ /**
+ * Defines whether the shape is pickable.
+ * @var {x3dom.fields.SFBool} isPickable
+ * @memberof x3dom.nodeTypes.X3DShapeNode
+ * @initvalue true
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFBool(ctx, 'isPickable', true);
+
+ /**
+ * Holds the id offset for MultiPart picking.
+ * @var {x3dom.fields.SFInt32} isPickable
+ * @memberof x3dom.nodeTypes.X3DShapeNode
+ * @initvalue 0
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFInt32(ctx, 'idOffset', 0);
+
+ /**
+ * Holds the appearance node.
+ * @var {x3dom.fields.SFNode} appearance
+ * @memberof x3dom.nodeTypes.X3DShapeNode
+ * @initvalue x3dom.nodeTypes.X3DAppearanceNode
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFNode('appearance', x3dom.nodeTypes.X3DAppearanceNode);
+
+ /**
+ * Holds the geometry node.
+ * @var {x3dom.fields.SFNode} geometry
+ * @memberof x3dom.nodeTypes.X3DShapeNode
+ * @initvalue x3dom.nodeTypes.X3DGeometryNode
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFNode('geometry', x3dom.nodeTypes.X3DGeometryNode);
+
+ this._objectID = 0;
+ this._shaderProperties = null;
+ this._clipPlanes = [];
+
+ // in WebGL-based renderer a clean-up function is attached
+ this._cleanupGLObjects = null;
+
+ this._dirty = {
+ positions: true,
+ normals: true,
+ texcoords: true,
+ colors: true,
+ specialAttribs: true, // e.g., particleSize, IDs,...
+ indexes: true,
+ texture: true,
+ material: true,
+ text: true,
+ shader: true
+ };
+
+ // FIXME; move somewhere else and allow generic values!!!
+ this._coordStrideOffset = [0, 0];
+ this._normalStrideOffset = [0, 0];
+ this._texCoordStrideOffset = [0, 0];
+ this._colorStrideOffset = [0, 0];
+
+ this._tessellationProperties = [];
+ },
+ {
+ collectDrawableObjects: function (transform, drawableCollection, singlePath, invalidateCache, planeMask, clipPlanes)
+ {
+ // attention, in contrast to other collectDrawableObjects()
+ // this one has boolean return type to better work with RSG
+ var graphState = this.graphState();
+
+ if (singlePath && (this._parentNodes.length > 1))
+ singlePath = false;
+
+ if (singlePath && (invalidateCache = invalidateCache || this.cacheInvalid()))
+ this.invalidateCache();
+
+ if (!this._cf.geometry.node ||
+ drawableCollection.cull(transform, graphState, singlePath, planeMask) <= 0) {
+ return false;
+ }
+
+ if (singlePath && !this._graph.globalMatrix)
+ this._graph.globalMatrix = transform;
+
+ if (this._clipPlanes.length != clipPlanes.length)
+ {
+ this._dirty.shader = true;
+ }
+
+ this._clipPlanes = clipPlanes;
+
+ drawableCollection.addShape(this, transform, graphState);
+
+ return true;
+ },
+
+ getVolume: function()
+ {
+ var vol = this._graph.volume;
+
+ if (!this.volumeValid() && this._vf.render)
+ {
+ var geo = this._cf.geometry.node;
+ var childVol = geo ? geo.getVolume() : null;
+
+ if (childVol && childVol.isValid())
+ vol.extendBounds(childVol.min, childVol.max);
+ }
+
+ return vol;
+ },
+
+ getCenter: function() {
+ var geo = this._cf.geometry.node;
+ return (geo ? geo.getCenter() : new x3dom.fields.SFVec3f(0,0,0));
+ },
+
+ getDiameter: function() {
+ var geo = this._cf.geometry.node;
+ return (geo ? geo.getDiameter() : 0);
+ },
+
+ doIntersect: function(line) {
+ return this._cf.geometry.node.doIntersect(line);
+ },
+
+ forceUpdateCoverage: function()
+ {
+ var geo = this._cf.geometry.node;
+ return (geo ? geo.forceUpdateCoverage() : false);
+ },
+
+ tessellationProperties: function()
+ {
+ // some geometries require offset and count into index array
+ var geo = this._cf.geometry.node;
+ if (geo && geo._indexOffset)
+ return geo._indexOffset; // IndexedTriangleStripSet
+ else
+ return this._tessellationProperties; // BVHRefiner-Patch
+ },
+
+ isLit: function() {
+ return this._cf.geometry.node._vf.lit;
+ },
+
+ isSolid: function() {
+ var twoSidedMat = (this._cf.appearance.node && this._cf.appearance.node._cf.material.node &&
+ x3dom.isa(this._cf.appearance.node._cf.material.node, x3dom.nodeTypes.TwoSidedMaterial));
+ return this._cf.geometry.node._vf.solid && !twoSidedMat;
+ },
+
+ isCCW: function() {
+ return this._cf.geometry.node._vf.ccw;
+ },
+
+ parentRemoved: function(parent) {
+ for (var i=0, n=this._childNodes.length; i<n; i++) {
+ var child = this._childNodes[i];
+ if (child) {
+ child.parentRemoved(this);
+ }
+ }
+
+ if (parent)
+ parent.invalidateVolume();
+ if (this._parentNodes.length > 0)
+ this.invalidateVolume();
+
+ // Cleans all GL objects for WebGL-based renderer
+ if (this._cleanupGLObjects) {
+ this._cleanupGLObjects();
+ }
+ },
+
+ unsetDirty: function () {
+ // vertex attributes
+ this._dirty.positions = false;
+ this._dirty.normals = false;
+ this._dirty.texcoords = false;
+ this._dirty.colors = false;
+ this._dirty.specialAttribs = false;
+ // indices/topology
+ this._dirty.indexes = false;
+ // appearance properties
+ this._dirty.texture = false;
+ this._dirty.material = false;
+ this._dirty.text = false;
+ this._dirty.shader = false;
+ },
+
+ unsetGeoDirty: function () {
+ this._dirty.positions = false;
+ this._dirty.normals = false;
+ this._dirty.texcoords = false;
+ this._dirty.colors = false;
+ this._dirty.specialAttribs = false;
+ this._dirty.indexes = false;
+ },
+
+ setAllDirty: function () {
+ // vertex attributes
+ this._dirty.positions = true;
+ this._dirty.normals = true;
+ this._dirty.texcoords = true;
+ this._dirty.colors = true;
+ this._dirty.specialAttribs = true;
+ // indices/topology
+ this._dirty.indexes = true;
+ // appearance properties
+ this._dirty.texture = true;
+ this._dirty.material = true;
+ this._dirty.text = true;
+ this._dirty.shader = true;
+ // finally invalidate volume
+ this.invalidateVolume();
+ },
+
+ setAppDirty: function () {
+ // appearance properties
+ this._dirty.texture = true;
+ this._dirty.material = true;
+ //this._dirty.text = true;
+ this._dirty.shader = true;
+ },
+
+ setGeoDirty: function () {
+ this._dirty.positions = true;
+ this._dirty.normals = true;
+ this._dirty.texcoords = true;
+ this._dirty.colors = true;
+ this._dirty.specialAttribs = true;
+ this._dirty.indexes = true;
+ // finally invalidate volume
+ this.invalidateVolume();
+ },
+
+ getShaderProperties: function(viewarea)
+ {
+ if (this._shaderProperties == null ||
+ this._dirty.shader == true ||
+ (this._webgl !== undefined &&
+ this._webgl.dirtyLighting != x3dom.Utils.checkDirtyLighting(viewarea) ) ||
+ x3dom.Utils.checkDirtyEnvironment(viewarea, this._shaderProperties) == true)
+ {
+ this._shaderProperties = x3dom.Utils.generateProperties(viewarea, this);
+
+ this._dirty.shader = false;
+ if (this._webgl !== undefined)
+ {
+ this._webgl.dirtyLighting = x3dom.Utils.checkDirtyLighting(viewarea);
+ }
+ }
+
+ return this._shaderProperties;
+ },
+
+ getTextures: function() {
+ var textures = [];
+
+ var appearance = this._cf.appearance.node;
+ if (appearance) {
+ var tex = appearance._cf.texture.node;
+ if(tex) {
+ if(x3dom.isa(tex, x3dom.nodeTypes.MultiTexture)) {
+ textures = textures.concat(tex.getTextures());
+ }
+ else {
+ textures.push(tex);
+ }
+ }
+
+ var shader = appearance._cf.shaders.nodes[0];
+ if(shader) {
+ if(x3dom.isa(shader, x3dom.nodeTypes.CommonSurfaceShader)) {
+ textures = textures.concat(shader.getTextures());
+ }
+ }
+ }
+
+ var geometry = this._cf.geometry.node;
+ if (geometry) {
+ if(x3dom.isa(geometry, x3dom.nodeTypes.ImageGeometry)) {
+ textures = textures.concat(geometry.getTextures());
+ }
+ else if(x3dom.isa(geometry, x3dom.nodeTypes.Text)) {
+ textures = textures.concat(geometry);
+ }
+ }
+
+ return textures;
+ }
+ }
+ )
+);
+
+/** @namespace x3dom.nodeTypes */
+/*
+ * X3DOM JavaScript Library
+ * http://www.x3dom.org
+ *
+ * (C)2009 Fraunhofer IGD, Darmstadt, Germany
+ * Dual licensed under the MIT and GPL
+ */
+
+/* ### Shape ### */
+x3dom.registerNodeType(
+ "Shape",
+ "Shape",
+ defineClass(x3dom.nodeTypes.X3DShapeNode,
+
+ /**
+ * Constructor for Shape
+ * @constructs x3dom.nodeTypes.Shape
+ * @x3d 3.3
+ * @component Shape
+ * @status full
+ * @extends x3dom.nodeTypes.X3DShapeNode
+ * @param {Object} [ctx=null] - context object, containing initial settings like namespace
+ * @classdesc The Shape node has two fields, appearance and geometry, that are used to create rendered objects in the world.
+ * The appearance field contains an Appearance node that specifies the visual attributes (e.g., material and texture) to be applied to the geometry.
+ * The geometry field contains a geometry node. The specified geometry node is rendered with the specified appearance nodes applied.
+ */
+ function (ctx) {
+ x3dom.nodeTypes.Shape.superClass.call(this, ctx);
+
+ },
+ {
+ nodeChanged: function () {
+ //TODO delete this if all works fine
+ if (!this._cf.appearance.node) {
+ //Unlit
+ //this.addChild(x3dom.nodeTypes.Appearance.defaultNode());
+ }
+ if (!this._cf.geometry.node) {
+ if (this._DEF)
+ x3dom.debug.logError("No geometry given in Shape/" + this._DEF);
+ }
+ else if (!this._objectID) {
+ this._objectID = ++x3dom.nodeTypes.Shape.objectID;
+ x3dom.nodeTypes.Shape.idMap.nodeID[this._objectID] = this;
+ }
+ this.invalidateVolume();
+ }
+ }
+ )
+);
+
+/** Static class ID counter (needed for caching) */
+x3dom.nodeTypes.Shape.shaderPartID = 0;
+
+/** Static class ID counter (needed for picking) */
+x3dom.nodeTypes.Shape.objectID = 0;
+
+/** Map for Shape node IDs (needed for picking) */
+x3dom.nodeTypes.Shape.idMap = {
+ nodeID: {},
+ remove: function(obj) {
+ for (var prop in this.nodeID) {
+ if (this.nodeID.hasOwnProperty(prop)) {
+ var val = this.nodeID[prop];
+ if (val._objectID && obj._objectID &&
+ val._objectID === obj._objectID)
+ {
+ delete this.nodeID[prop];
+ x3dom.debug.logInfo("Unreg " + val._objectID);
+ // FIXME; handle node removal to unreg from map,
+ // and put free'd ID back to ID pool for reuse
+ }
+ }
+ }
+ }
+};
+/** @namespace x3dom.nodeTypes */
+/*
+ * X3DOM JavaScript Library
+ * http://www.x3dom.org
+ *
+ * (C)2009 Fraunhofer IGD, Darmstadt, Germany
+ * Dual licensed under the MIT and GPL
+ */
+
+/* ### X3DLightNode ### */
+x3dom.registerNodeType(
+ "X3DLightNode",
+ "Lighting",
+ defineClass(x3dom.nodeTypes.X3DChildNode,
+
+ /**
+ * Constructor for X3DLightNode
+ * @constructs x3dom.nodeTypes.X3DLightNode
+ * @x3d 3.3
+ * @component Lighting
+ * @status full
+ * @extends x3dom.nodeTypes.X3DChildNode
+ * @param {Object} [ctx=null] - context object, containing initial settings like namespace
+ * @classdesc The X3DLightNode abstract node type is the base type from which all node types that serve as light sources are derived.
+ */
+ function (ctx) {
+ x3dom.nodeTypes.X3DLightNode.superClass.call(this, ctx);
+
+ if (ctx)
+ ctx.doc._nodeBag.lights.push(this);
+ else
+ x3dom.debug.logWarning("X3DLightNode: No runtime context found!");
+
+ this._lightID = 0;
+ this._dirty = true;
+
+
+ /**
+ * The ambientIntensity specifies the intensity of the ambient emission from the light. Light intensity may range from 0.0 (no light emission) to 1.0 (full intensity).
+ * @var {x3dom.fields.SFFloat} ambientIntensity
+ * @range [0, 1]
+ * @memberof x3dom.nodeTypes.X3DLightNode
+ * @initvalue 0
+ * @field x3d
+ * @instance
+ */
+ this.addField_SFFloat(ctx, 'ambientIntensity', 0);
+
+ /**
+ * The color field specifies the spectral colour properties of both the direct and ambient light emission as an RGB value.
+ * @var {x3dom.fields.SFColor} color
+ * @range [0, 1]
+ * @memberof x3dom.nodeTypes.X3DLightNode
+ * @initvalue 1,1,1
+ * @field x3d
+ * @instance
+ */
+ this.addField_SFColor(ctx, 'color', 1, 1, 1);
+
+ /**
+ * The intensity field specifies the brightness of the direct emission from the light. Light intensity may range from 0.0 (no light emission) to 1.0 (full intensity).
+ * @var {x3dom.fields.SFFloat} intensity
+ * @range [0, 1]
+ * @memberof x3dom.nodeTypes.X3DLightNode
+ * @initvalue 1
+ * @field x3d
+ * @instance
+ */
+ this.addField_SFFloat(ctx, 'intensity', 1);
+
+ /**
+ * Specifies whether the light is global or scoped.
+ * Global lights illuminate all objects that fall within their volume of lighting influence.
+ * Scoped lights only illuminate objects that are in the same transformation hierarchy as the light; i.e., only the children and descendants of its enclosing parent group are illuminated.
+ * @var {x3dom.fields.SFBool} global
+ * @memberof x3dom.nodeTypes.X3DLightNode
+ * @initvalue false
+ * @field x3d
+ * @instance
+ */
+ this.addField_SFBool(ctx, 'global', false);
+
+ /**
+ * The on field specifies whether the light is enabled or disabled.
+ * @var {x3dom.fields.SFBool} on
+ * @memberof x3dom.nodeTypes.X3DLightNode
+ * @initvalue true
+ * @field x3d
+ * @instance
+ */
+ this.addField_SFBool(ctx, 'on', true);
+
+ /**
+ * Defines the attenuation of the shadows
+ * @var {x3dom.fields.SFFloat} shadowIntensity
+ * @range [o, 1]
+ * @memberof x3dom.nodeTypes.X3DLightNode
+ * @initvalue 0
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFFloat(ctx, 'shadowIntensity', 0);
+
+ /**
+ * Specifies the resolution of the used shadow map.
+ * @var {x3dom.fields.SFInt32} shadowMapSize
+ * @range [0, inf]
+ * @memberof x3dom.nodeTypes.X3DLightNode
+ * @initvalue 1024
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFInt32(ctx, 'shadowMapSize', 1024);
+
+ /**
+ * Sets the smoothness of the shadow umbra.
+ * @var {x3dom.fields.SFInt32} shadowFilterSize
+ * @memberof x3dom.nodeTypes.X3DLightNode
+ * @initvalue 0
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFInt32(ctx, 'shadowFilterSize', 0);
+
+ /**
+ * Defines the shadow offset for the back projection of the shadow map.
+ * @var {x3dom.fields.SFFloat} shadowOffset
+ * @memberof x3dom.nodeTypes.X3DLightNode
+ * @initvalue 0
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFFloat(ctx, 'shadowOffset', 0);
+
+ /**
+ * Specifies the placement of the near plane of the light projection.
+ * Objects that are closer to the light source than the near plane do not cast shadows.
+ * If the zNear value is not set, the near plane is placed automatically.
+ * @var {x3dom.fields.SFFloat} zNear
+ * @range -1 or [0, inf]
+ * @memberof x3dom.nodeTypes.X3DLightNode
+ * @initvalue -1
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFFloat(ctx, 'zNear', -1);
+
+ /**
+ * Specifies the placement of the far plane of the light projection.
+ * Objects that are farther away from the light source than the far plane do not cast shadows.
+ * If the zFar value is not set, the far plane is placed automatically.
+ * @var {x3dom.fields.SFFloat} zFar
+ * @range -1 or [0, inf]
+ * @memberof x3dom.nodeTypes.X3DLightNode
+ * @initvalue -1
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFFloat(ctx, 'zFar', -1);
+
+ },
+ {
+ getViewMatrix: function(vec) {
+ return x3dom.fields.SFMatrix4f.identity;
+ },
+
+ nodeChanged: function () {
+ if(!this._lightID) {
+ this._lightID = ++x3dom.nodeTypes.X3DLightNode.lightID;
+ }
+ },
+
+ fieldChanged: function(fieldName)
+ {
+ if (this._vf.hasOwnProperty(fieldName)) {
+ this._dirty = true;
+ }
+ },
+
+ parentRemoved: function(parent)
+ {
+ if (this._parentNodes.length === 0) {
+ var doc = this.findX3DDoc();
+
+ for (var i=0, n=doc._nodeBag.lights.length; i<n; i++) {
+ if (doc._nodeBag.lights[i] === this) {
+ doc._nodeBag.lights.splice(i, 1);
+ }
+ }
+ }
+ }
+ }
+ )
+);
+
+/** Static class ID counter (needed for flash performance up) */
+x3dom.nodeTypes.X3DLightNode.lightID = 0;
+
+/** @namespace x3dom.nodeTypes */
+/*
+ * X3DOM JavaScript Library
+ * http://www.x3dom.org
+ *
+ * (C)2009 Fraunhofer IGD, Darmstadt, Germany
+ * Dual licensed under the MIT and GPL
+ */
+
+/* ### DirectionalLight ### */
+x3dom.registerNodeType(
+ "DirectionalLight",
+ "Lighting",
+ defineClass(x3dom.nodeTypes.X3DLightNode,
+
+ /**
+ * Constructor for DirectionalLight
+ * @constructs x3dom.nodeTypes.DirectionalLight
+ * @x3d 3.3
+ * @component Lighting
+ * @status experimental
+ * @extends x3dom.nodeTypes.X3DLightNode
+ * @param {Object} [ctx=null] - context object, containing initial settings like namespace
+ * @classdesc The DirectionalLight node defines a directional light source that illuminates along rays parallel to a given 3-dimensional vector.
+ * A directional light source illuminates only the objects in its enclosing parent group.
+ * The light may illuminate everything within this coordinate system, including all children and descendants of its parent group.
+ * The accumulated transformations of the parent nodes affect the light.
+ * DirectionalLight nodes do not attenuate with distance.
+ */
+ function (ctx) {
+ x3dom.nodeTypes.DirectionalLight.superClass.call(this, ctx);
+
+
+ /**
+ * The direction field specifies the direction vector of the illumination emanating from the light source in the local coordinate system.
+ * @var {x3dom.fields.SFVec3f} direction
+ * @memberof x3dom.nodeTypes.DirectionalLight
+ * @initvalue 0,0,-1
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFVec3f(ctx, 'direction', 0, 0, -1);
+
+ /**
+ *
+ * @var {x3dom.fields.SFInt32} shadowCascades
+ * @memberof x3dom.nodeTypes.DirectionalLight
+ * @initvalue 1
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFInt32(ctx, 'shadowCascades', 1);
+
+ /**
+ *
+ * @var {x3dom.fields.SFFloat} shadowSplitFactor
+ * @memberof x3dom.nodeTypes.DirectionalLight
+ * @initvalue 1
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFFloat(ctx, 'shadowSplitFactor', 1);
+
+ /**
+ *
+ * @var {x3dom.fields.SFFloat} shadowSplitOffset
+ * @memberof x3dom.nodeTypes.DirectionalLight
+ * @initvalue 0.1
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFFloat(ctx, 'shadowSplitOffset', 0.1);
+
+ },
+ {
+ getViewMatrix: function(vec) {
+ var dir = this.getCurrentTransform().multMatrixVec(this._vf.direction).normalize();
+ var orientation = x3dom.fields.Quaternion.rotateFromTo(
+ new x3dom.fields.SFVec3f(0, 0, -1), dir);
+ return orientation.toMatrix().transpose().
+ mult(x3dom.fields.SFMatrix4f.translation(vec.negate()));
+ }
+ }
+ )
+);
+/** @namespace x3dom.nodeTypes */
+/*
+ * X3DOM JavaScript Library
+ * http://www.x3dom.org
+ *
+ * (C)2009 Fraunhofer IGD, Darmstadt, Germany
+ * Dual licensed under the MIT and GPL
+ */
+
+/* ### PointLight ### */
+x3dom.registerNodeType(
+ "PointLight",
+ "Lighting",
+ defineClass(x3dom.nodeTypes.X3DLightNode,
+
+ /**
+ * Constructor for PointLight
+ * @constructs x3dom.nodeTypes.PointLight
+ * @x3d 3.3
+ * @component Lighting
+ * @status full
+ * @extends x3dom.nodeTypes.X3DLightNode
+ * @param {Object} [ctx=null] - context object, containing initial settings like namespace
+ * @classdesc The PointLight node specifies a point light source at a 3D location in the local coordinate system.
+ * A point light source emits light equally in all directions; that is, it is omnidirectional.
+ * PointLight nodes are specified in the local coordinate system and are affected by ancestor transformations.
+ */
+ function (ctx) {
+ x3dom.nodeTypes.PointLight.superClass.call(this, ctx);
+
+
+ /**
+ * PointLight node's illumination falls off with distance as specified by three attenuation coefficients. The attenuation factor is:
+ * 1/max(attenuation[0] + attenuation[1] × r + attenuation[2] × r^2, 1)
+ * where r is the distance from the light to the surface being illuminated.
+ * The default is no attenuation.
+ * An attenuation value of (0, 0, 0) is identical to (1, 0, 0). Attenuation values shall be greater than or equal to zero.
+ * @var {x3dom.fields.SFVec3f} attenuation
+ * @memberof x3dom.nodeTypes.PointLight
+ * @initvalue 1,0,0
+ * @field x3d
+ * @instance
+ */
+ this.addField_SFVec3f(ctx, 'attenuation', 1, 0, 0);
+
+ /**
+ * The position of the Light
+ * @var {x3dom.fields.SFVec3f} location
+ * @memberof x3dom.nodeTypes.PointLight
+ * @initvalue 0,0,0
+ * @field x3d
+ * @instance
+ */
+ this.addField_SFVec3f(ctx, 'location', 0, 0, 0);
+
+ /**
+ * A PointLight node illuminates geometry within radius length base units of its location.
+ * Radius is affected by ancestors' transformations.
+ * @var {x3dom.fields.SFFloat} radius
+ * @memberof x3dom.nodeTypes.PointLight
+ * @initvalue 100
+ * @field x3d
+ * @instance
+ */
+ this.addField_SFFloat(ctx, 'radius', 100);
+
+ this._vf.global = true;
+
+ },
+ {
+ getViewMatrix: function(vec) {
+ var pos = this.getCurrentTransform().multMatrixPnt(this._vf.location);
+ var orientation = x3dom.fields.Quaternion.rotateFromTo(
+ new x3dom.fields.SFVec3f(0, 0, -1), vec);
+ return orientation.toMatrix().transpose().
+ mult(x3dom.fields.SFMatrix4f.translation(pos.negate()));
+ }
+ }
+ )
+);
+/** @namespace x3dom.nodeTypes */
+/*
+ * X3DOM JavaScript Library
+ * http://www.x3dom.org
+ *
+ * (C)2009 Fraunhofer IGD, Darmstadt, Germany
+ * Dual licensed under the MIT and GPL
+ */
+
+/* ### SpotLight ### */
+x3dom.registerNodeType(
+ "SpotLight",
+ "Lighting",
+ defineClass(x3dom.nodeTypes.X3DLightNode,
+
+ /**
+ * Constructor for SpotLight
+ * @constructs x3dom.nodeTypes.SpotLight
+ * @x3d 3.3
+ * @component Lighting
+ * @status full
+ * @extends x3dom.nodeTypes.X3DLightNode
+ * @param {Object} [ctx=null] - context object, containing initial settings like namespace
+ * @classdesc The SpotLight node defines a light source that emits light from a specific point along a specific direction vector and constrained within a solid angle.
+ * Spotlights may illuminate geometry nodes that respond to light sources and intersect the solid angle defined by the SpotLight.
+ * Spotlight nodes are specified in the local coordinate system and are affected by ancestors' transformations.
+ */
+ function (ctx) {
+ x3dom.nodeTypes.SpotLight.superClass.call(this, ctx);
+
+
+ /**
+ * The direction field specifies the direction vector of the light's central axis defined in the local coordinate system.
+ * @var {x3dom.fields.SFVec3f} direction
+ * @memberof x3dom.nodeTypes.SpotLight
+ * @initvalue 0,0,-1
+ * @field x3d
+ * @instance
+ */
+ this.addField_SFVec3f(ctx, 'direction', 0, 0, -1);
+
+ /**
+ * SpotLight node's illumination falls off with distance as specified by three attenuation coefficients. The attenuation factor is:
+ * 1/max(attenuation[0] + attenuation[1] × r + attenuation[2] × r^2, 1)
+ * where r is the distance from the light to the surface being illuminated.
+ * The default is no attenuation.
+ * An attenuation value of (0, 0, 0) is identical to (1, 0, 0). Attenuation values shall be greater than or equal to zero.
+ * @var {x3dom.fields.SFVec3f} attenuation
+ * @range [0, inf]
+ * @memberof x3dom.nodeTypes.SpotLight
+ * @initvalue 1,0,0
+ * @field x3d
+ * @instance
+ */
+ this.addField_SFVec3f(ctx, 'attenuation', 1, 0, 0);
+
+ /**
+ * The location field specifies a translation offset of the centre point of the light source from the light's local coordinate system origin.
+ * This point is the apex of the solid angle which bounds light emission from the given light source.
+ * Location is affected by ancestors' transformations.
+ * @var {x3dom.fields.SFVec3f} location
+ * @memberof x3dom.nodeTypes.SpotLight
+ * @initvalue 0,0,0
+ * @field x3d
+ * @instance
+ */
+ this.addField_SFVec3f(ctx, 'location', 0, 0, 0);
+
+ /**
+ * The radius field specifies the radial extent of the solid angle and the maximum distance from location that may be illuminated by the light source.
+ * The light source does not emit light outside this radius. The radius shall be greater than or equal to zero.
+ * Radius is affected by ancestors' transformations.
+ * @var {x3dom.fields.SFFloat} radius
+ * @range [0, inf]
+ * @memberof x3dom.nodeTypes.SpotLight
+ * @initvalue 100
+ * @field x3d
+ * @instance
+ */
+ this.addField_SFFloat(ctx, 'radius', 100);
+
+ /**
+ * The beamWidth field specifies an inner solid angle in which the light source emits light at uniform full intensity.
+ * The light source's emission intensity drops off from the inner solid angle (beamWidth) to the outer solid angle (cutOffAngle).
+ * If the beamWidth is greater than the cutOffAngle, beamWidth is defined to be equal to the cutOffAngle and the light source emits full intensity within the entire solid angle defined by cutOffAngle.
+ * @var {x3dom.fields.SFFloat} beamWidth
+ * @range [0, pi/2]
+ * @memberof x3dom.nodeTypes.SpotLight
+ * @initvalue 1.5707963
+ * @field x3d
+ * @instance
+ */
+ this.addField_SFFloat(ctx, 'beamWidth', 1.5707963);
+
+ /**
+ * The cutOffAngle field specifies the outer bound of the solid angle. The light source does not emit light outside of this solid angle.
+ * The light source's emission intensity drops off from the inner solid angle (beamWidth) to the outer solid angle (cutOffAngle).
+ * If the beamWidth is greater than the cutOffAngle, beamWidth is defined to be equal to the cutOffAngle and the light source emits full intensity within the entire solid angle defined by cutOffAngle.
+ * @range [0, pi/2]
+ * @var {x3dom.fields.SFFloat} cutOffAngle
+ * @memberof x3dom.nodeTypes.SpotLight
+ * @initvalue 1.5707963
+ * @field x3d
+ * @instance
+ */
+ this.addField_SFFloat(ctx, 'cutOffAngle', 1.5707963);
+
+ /**
+ *
+ * @var {x3dom.fields.SFInt32} shadowCascades
+ * @memberof x3dom.nodeTypes.SpotLight
+ * @initvalue 1
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFInt32(ctx, 'shadowCascades', 1);
+
+ /**
+ *
+ * @var {x3dom.fields.SFFloat} shadowSplitFactor
+ * @memberof x3dom.nodeTypes.SpotLight
+ * @initvalue 1
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFFloat(ctx, 'shadowSplitFactor', 1);
+
+ /**
+ *
+ * @var {x3dom.fields.SFFloat} shadowSplitOffset
+ * @memberof x3dom.nodeTypes.SpotLight
+ * @initvalue 0.1
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFFloat(ctx, 'shadowSplitOffset', 0.1);
+
+ this._vf.global = true;
+
+ },
+ {
+ getViewMatrix: function(vec) {
+ var pos = this.getCurrentTransform().multMatrixPnt(this._vf.location);
+ var dir = this.getCurrentTransform().multMatrixVec(this._vf.direction).normalize();
+ var orientation = x3dom.fields.Quaternion.rotateFromTo(
+ new x3dom.fields.SFVec3f(0, 0, -1), dir);
+ return orientation.toMatrix().transpose().
+ mult(x3dom.fields.SFMatrix4f.translation(pos.negate()));
+ }
+ }
+ )
+);
+
+/** @namespace x3dom.nodeTypes */
+/*
+ * X3DOM JavaScript Library
+ * http://www.x3dom.org
+ *
+ * (C)2009 Fraunhofer IGD, Darmstadt, Germany
+ * Dual licensed under the MIT and GPL
+ */
+
+/* ### X3DFollowerNode ### */
+x3dom.registerNodeType(
+ "X3DFollowerNode",
+ "Followers",
+ defineClass(x3dom.nodeTypes.X3DChildNode,
+
+ /**
+ * Constructor for X3DFollowerNode
+ * @constructs x3dom.nodeTypes.X3DFollowerNode
+ * @x3d 3.3
+ * @component Followers
+ * @status experimental
+ * @extends x3dom.nodeTypes.X3DChildNode
+ * @param {Object} [ctx=null] - context object, containing initial settings like namespace
+ * @classdesc An X3DFollowerNode maintains an internal state that consists of a current value and a destination
+ * value. Both values are of the same data type into which the term [S|M]F<type> evaluatesfor a given
+ * specialization. It is the 'data type of the node'. In certain cases of usage, the terms input and output fit
+ * better for destination value and current value, respectively.
+ * Whenever the current value differs from the destination value, the current value gradually changes until it
+ * reaches the destination value producing a smooth transition. It generally moves towards the destination
+ * value but, if a transition triggered by a prevous destination value is still in progress, it may take a
+ * short while until the movement becomes a movement towards the new destination value.
+ */
+ function (ctx) {
+ x3dom.nodeTypes.X3DFollowerNode.superClass.call(this, ctx);
+
+ if (ctx)
+ ctx.doc._nodeBag.followers.push(this);
+ else
+ x3dom.debug.logWarning("X3DFollowerNode: No runtime context found!");
+
+
+ /**
+ * isActive shows if the sensor is active
+ * @var {x3dom.fields.SFBool} isActive
+ * @memberof x3dom.nodeTypes.X3DFollowerNode
+ * @initvalue false
+ * @field x3d
+ * @instance
+ */
+ this.addField_SFBool(ctx, 'isActive', false);
+
+ // http://www.web3d.org/files/specifications/19775-1/V3.3/Part01/components/followers.html
+ // [S|M]F<type> [in] set_destination
+ // [S|M]F<type> [in] set_value
+ // [S|M]F<type> [out] value
+ // SFBool [out] isActive
+ // [S|M]F<type> [] initialDestination
+ // [S|M]F<type> [] initialValue
+
+ this._eps = x3dom.fields.Eps; //0.001;
+
+ },
+ {
+ parentRemoved: function(parent)
+ {
+ if (this._parentNodes.length === 0) {
+ var doc = this.findX3DDoc();
+
+ for (var i=0, n=doc._nodeBag.followers.length; i<n; i++) {
+ if (doc._nodeBag.followers[i] === this) {
+ doc._nodeBag.followers.splice(i, 1);
+ }
+ }
+ }
+ },
+
+ tick: function(t) {
+ return false;
+ },
+
+ stepResponse: function(t)
+ {
+ if (t <= 0) {
+ return 0;
+ }
+
+ if (t >= this._vf.duration) {
+ return 1;
+ }
+
+ // When optimizing for speed, the above two if(.) cases can be omitted,
+ // as this function will not be called for values outside of 0..duration.
+ return this.stepResponseCore(t / this._vf.duration);
+ },
+
+ // This function defines the shape of how the output responds to the initialDestination.
+ // It must accept values for T in the range 0 <= T <= 1.
+ // In this._vf.order to create a smooth animation, it should return 0 for T == 0,
+ // 1 for T == 1 and be sufficient smooth in the range 0 <= T <= 1.
+ //
+ // It should be optimized for speed, in this._vf.order for high performance. It's
+ // executed _buffer.length + 1 times each simulation tick.
+ stepResponseCore: function(T)
+ {
+ return 0.5 - 0.5 * Math.cos(T * Math.PI);
+ }
+ }
+ )
+);
+/** @namespace x3dom.nodeTypes */
+/*
+ * X3DOM JavaScript Library
+ * http://www.x3dom.org
+ *
+ * (C)2009 Fraunhofer IGD, Darmstadt, Germany
+ * Dual licensed under the MIT and GPL
+ */
+
+/* ### X3DChaserNode ### */
+x3dom.registerNodeType(
+ "X3DChaserNode",
+ "Followers",
+ defineClass(x3dom.nodeTypes.X3DFollowerNode,
+
+ /**
+ * Constructor for X3DChaserNode
+ * @constructs x3dom.nodeTypes.X3DChaserNode
+ * @x3d 3.3
+ * @component Followers
+ * @status experimental
+ * @extends x3dom.nodeTypes.X3DFollowerNode
+ * @param {Object} [ctx=null] - context object, containing initial settings like namespace
+ * @classdesc The X3DChaserNode abstract node type calculates the output on value_changed as a finite impulse
+ * response (FIR).
+ */
+ function (ctx) {
+ x3dom.nodeTypes.X3DChaserNode.superClass.call(this, ctx);
+
+
+ /**
+ * Duration of the transition
+ * @var {x3dom.fields.SFTime} duration
+ * @memberof x3dom.nodeTypes.X3DChaserNode
+ * @initvalue 1
+ * @field x3d
+ * @instance
+ */
+ this.addField_SFTime(ctx, 'duration', 1);
+
+ this._initDone = false;
+ this._stepTime = 0;
+ this._currTime = 0;
+ this._bufferEndTime = 0;
+ this._numSupports = 60;
+
+ }
+ )
+);
+/** @namespace x3dom.nodeTypes */
+/*
+ * X3DOM JavaScript Library
+ * http://www.x3dom.org
+ *
+ * (C)2009 Fraunhofer IGD, Darmstadt, Germany
+ * Dual licensed under the MIT and GPL
+ */
+
+/* ### X3DDamperNode ### */
+x3dom.registerNodeType(
+ "X3DDamperNode",
+ "Followers",
+ defineClass(x3dom.nodeTypes.X3DFollowerNode,
+
+ /**
+ * Constructor for X3DDamperNode
+ * @constructs x3dom.nodeTypes.X3DDamperNode
+ * @x3d 3.3
+ * @component Followers
+ * @status experimental
+ * @extends x3dom.nodeTypes.X3DFollowerNode
+ * @param {Object} [ctx=null] - context object, containing initial settings like namespace
+ * @classdesc The X3DDamperNode abstract node type creates an IIR response that approaches the destination
+ * value according to the shape of the e-function only asymptotically but very quickly. An X3DDamperNode node
+ * is parameterized by the tau, order and tolerance fields. Internally, it consists of a set of linear
+ * first-order filters each of which processes the output of the previous filter.
+ */
+ function (ctx) {
+ x3dom.nodeTypes.X3DDamperNode.superClass.call(this, ctx);
+
+
+ /**
+ * The field tau specifies the time-constant of the internal filters and thus the speed that the output of
+ * an X3DDamperNode responds to the input. A value of zero for tau means immediate response and the events
+ * received on set_destination are forwarded directly. The field tau specifies how long it takes the output
+ * of an internal filter to reach the value of its input by 63% (1 - 1/e). The remainder after that period
+ * is reduced by 63% during another period of tau seconds provided that the input of the filter does not
+ * change. This behavior can be exposed if order is set to one.
+ * @var {x3dom.fields.SFTime} tau
+ * @memberof x3dom.nodeTypes.X3DDamperNode
+ * @initvalue 0.3
+ * @range [0,inf)
+ * @field x3d
+ * @instance
+ */
+ this.addField_SFTime(ctx, 'tau', 0.3);
+
+ /**
+ * If tolerance is set to its default value -1, the browser implementation is allowed to find a good way for
+ * detecting the end of a transition. Browsers that do not have an elaborate algorithm can just use .001 as
+ * the tolerance value instead. If a value larger than zero is specified for tolerance, the browser shall
+ * calculate the difference between output and input for each internal filter being used and stop the
+ * animation only when all filters fall below that limit or are equal to it. If zero is specified for
+ * tolerance, a transition should be stopped only if input and output match exactly for all internal
+ * filters.
+ * @var {x3dom.fields.SFFloat} tolerance
+ * @memberof x3dom.nodeTypes.X3DDamperNode
+ * @initvalue -1
+ * @range -1 or [0,inf)
+ * @field x3d
+ * @instance
+ */
+ this.addField_SFFloat(ctx, 'tolerance', -1);
+
+ /**
+ * The order field specifies the smoothness of the transition.
+ * @var {x3dom.fields.SFInt32} order
+ * @memberof x3dom.nodeTypes.X3DDamperNode
+ * @initvalue 3
+ * @range [0..5]
+ * @field x3d
+ * @instance
+ */
+ this.addField_SFInt32(ctx, 'order', 3);
+
+ this._eps = this._vf.tolerance < 0 ? this._eps : this._vf.tolerance;
+ this._lastTick = 0;
+
+ }
+ )
+);
+/** @namespace x3dom.nodeTypes */
+/*
+ * X3DOM JavaScript Library
+ * http://www.x3dom.org
+ *
+ * (C)2009 Fraunhofer IGD, Darmstadt, Germany
+ * Dual licensed under the MIT and GPL
+ */
+
+/* ### ColorChaser ### */
+x3dom.registerNodeType(
+ "ColorChaser",
+ "Followers",
+ defineClass(x3dom.nodeTypes.X3DChaserNode,
+
+ /**
+ * Constructor for ColorChaser
+ * @constructs x3dom.nodeTypes.ColorChaser
+ * @x3d 3.3
+ * @component Followers
+ * @status experimental
+ * @extends x3dom.nodeTypes.X3DChaserNode
+ * @param {Object} [ctx=null] - context object, containing initial settings like namespace
+ * @classdesc The ColorChaser animates transitions for single color values. Whenever the set_destination
+ * field receives a floating point number, the value_changed creates a transition from its current value to
+ * the newly set number. It creates a smooth transition that ends duration seconds after the last number has
+ * been received.
+ */
+ function (ctx) {
+ x3dom.nodeTypes.ColorChaser.superClass.call(this, ctx);
+
+
+ /**
+ * The field initialDestination should be set to the same value as initialValue unless a transition to a
+ * certain value is to be created right after the scene is loaded or right after the ColorChaser node is
+ * created dynamically.
+ * @var {x3dom.fields.SFColor} initialDestination
+ * @memberof x3dom.nodeTypes.ColorChaser
+ * @initvalue 0.8,0.8,0.8
+ * @range [0,1]
+ * @field x3d
+ * @instance
+ */
+ this.addField_SFColor(ctx, 'initialDestination', 0.8, 0.8, 0.8);
+
+ /**
+ * The field initialValue can be used to set the initial value.
+ * @var {x3dom.fields.SFColor} initialValue
+ * @memberof x3dom.nodeTypes.ColorChaser
+ * @initvalue 0.8,0.8,0.8
+ * @range [0,1]
+ * @field x3d
+ * @instance
+ */
+ this.addField_SFColor(ctx, 'initialValue', 0.8, 0.8, 0.8);
+
+
+ /**
+ * The current color value
+ * @var {x3dom.fields.SFColor} value
+ * @memberof x3dom.nodeTypes.ColorChaser
+ * @initvalue 0,0,0
+ * @range [0,1]
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFColor(ctx, 'value', 0, 0, 0);
+
+ /**
+ * The target color value
+ * @var {x3dom.fields.SFColor} destination
+ * @memberof x3dom.nodeTypes.ColorChaser
+ * @initvalue 0,0,0
+ * @range [0,1]
+ * @field x3d
+ * @instance
+ */
+ this.addField_SFColor(ctx, 'destination', 0, 0, 0);
+
+ this._buffer = new x3dom.fields.MFColor();
+ this._previousValue = new x3dom.fields.SFColor(0, 0, 0);
+ this._value = new x3dom.fields.SFColor(0, 0, 0);
+
+ this.initialize();
+
+ },
+ {
+ fieldChanged: function(fieldName)
+ {
+ if (fieldName.indexOf("destination") >= 0)
+ {
+ this.initialize();
+ this.updateBuffer(this._currTime);
+
+ if (!this._vf.isActive) {
+ this.postMessage('isActive', true);
+ }
+ }
+ else if (fieldName.indexOf("value") >= 0)
+ {
+ this.initialize();
+
+ this._previousValue.setValues(this._vf.value);
+ for (var C=1; C<this._buffer.length; C++) {
+ this._buffer[C].setValues(this._vf.value);
+ }
+
+ this.postMessage('value', this._vf.value);
+
+ if (!this._vf.isActive) {
+ this.postMessage('isActive', true);
+ }
+ }
+ },
+
+ /** The following handler code is copy & paste from PositionChaser
+ */
+ initialize: function()
+ {
+ if (!this._initDone)
+ {
+ this._initDone = true;
+
+ this._vf.destination = this._vf.initialDestination;
+
+ this._buffer.length = this._numSupports;
+
+ this._buffer[0] = this._vf.initialDestination;
+ for (var C=1; C<this._buffer.length; C++) {
+ this._buffer[C] = this._vf.initialValue;
+ }
+
+ this._previousValue = this._vf.initialValue;
+
+ this._stepTime = this._vf.duration / this._numSupports;
+
+ var active = !this._buffer[0].equals(this._buffer[1], this._eps);
+ if (this._vf.isActive !== active) {
+ this.postMessage('isActive', active);
+ }
+ }
+ },
+
+ tick: function(now)
+ {
+ this.initialize();
+ this._currTime = now;
+
+ //if (!this._vf.isActive)
+ // return false;
+
+ if (!this._bufferEndTime)
+ {
+ this._bufferEndTime = now; // on init
+
+ this._value = this._vf.initialValue;
+
+ this.postMessage('value', this._value);
+
+ return true;
+ }
+
+ var Frac = this.updateBuffer(now);
+
+ var Output = this._previousValue;
+
+ var DeltaIn = this._buffer[this._buffer.length - 1].subtract(this._previousValue);
+
+ var DeltaOut = DeltaIn.multiply(this.stepResponse((this._buffer.length - 1 + Frac) * this._stepTime));
+
+ Output = Output.add(DeltaOut);
+
+ for (var C=this._buffer.length - 2; C>=0; C--)
+ {
+ DeltaIn = this._buffer[C].subtract(this._buffer[C + 1]);
+
+ DeltaOut = DeltaIn.multiply(this.stepResponse((C + Frac) * this._stepTime));
+
+ Output = Output.add(DeltaOut);
+ }
+
+ if ( !Output.equals(this._value, this._eps) ) {
+ this._value.setValues(Output);
+
+ this.postMessage('value', this._value);
+ }
+ else {
+ this.postMessage('isActive', false);
+ }
+
+ return this._vf.isActive;
+ },
+
+ updateBuffer: function(now)
+ {
+ var Frac = (now - this._bufferEndTime) / this._stepTime;
+ var C;
+ var NumToShift;
+ var Alpha;
+
+ if (Frac >= 1)
+ {
+ NumToShift = Math.floor(Frac);
+ Frac -= NumToShift;
+
+ if( NumToShift < this._buffer.length)
+ {
+ this._previousValue = this._buffer[this._buffer.length - NumToShift];
+
+ for (C=this._buffer.length - 1; C>=NumToShift; C--) {
+ this._buffer[C] = this._buffer[C - NumToShift];
+ }
+
+ for (C=0; C<NumToShift; C++)
+ {
+ Alpha = C / NumToShift;
+
+ this._buffer[C] = this._buffer[NumToShift].multiply(Alpha).add(this._vf.destination.multiply((1 - Alpha)));
+ }
+ }
+ else
+ {
+ this._previousValue = (NumToShift == this._buffer.length) ? this._buffer[0] : this._vf.destination;
+
+ for (C= 0; C<this._buffer.length; C++) {
+ this._buffer[C] = this._vf.destination;
+ }
+ }
+ this._bufferEndTime += NumToShift * this._stepTime;
+ }
+ return Frac;
+ }
+ }
+ )
+);
+/** @namespace x3dom.nodeTypes */
+/*
+ * X3DOM JavaScript Library
+ * http://www.x3dom.org
+ *
+ * (C)2009 Fraunhofer IGD, Darmstadt, Germany
+ * Dual licensed under the MIT and GPL
+ */
+
+/* ### ColorDamper ### */
+x3dom.registerNodeType(
+ "ColorDamper",
+ "Followers",
+ defineClass(x3dom.nodeTypes.X3DDamperNode,
+
+ /**
+ * Constructor for ColorDamper
+ * @constructs x3dom.nodeTypes.ColorDamper
+ * @x3d 3.3
+ * @component Followers
+ * @status experimental
+ * @extends x3dom.nodeTypes.X3DDamperNode
+ * @param {Object} [ctx=null] - context object, containing initial settings like namespace
+ * @classdesc The ColorDamper animates color values. Whenever the it receives a color, the ColorDamper node
+ * creates a transition from the current color to the newly set color. The transition created approaches the
+ * newly set position asymptotically during a time period of approximately three to four times the value of
+ * the field tau depending on the desired accuracy and the value of order. The order field specifies the
+ * smoothness of the transition.
+ */
+ function (ctx) {
+ x3dom.nodeTypes.ColorDamper.superClass.call(this, ctx);
+
+
+ /**
+ * The field initialDestination should be set to the same value than initialValue unless a transition to a
+ * certain color is to be created right after the scene is loaded or right after the ColorDamper node is
+ * created dynamically.
+ * @var {x3dom.fields.SFColor} initialDestination
+ * @memberof x3dom.nodeTypes.ColorDamper
+ * @initvalue 0.8,0.8,0.8
+ * @range [0,1]
+ * @field x3d
+ * @instance
+ */
+ this.addField_SFColor(ctx, 'initialDestination', 0.8, 0.8, 0.8);
+
+ /**
+ * The field initialValue can be used to set the initial color.
+ * @var {x3dom.fields.SFColor} initialValue
+ * @memberof x3dom.nodeTypes.ColorDamper
+ * @initvalue 0.8,0.8,0.8
+ * @range [0,1]
+ * @field x3d
+ * @instance
+ */
+ this.addField_SFColor(ctx, 'initialValue', 0.8, 0.8, 0.8);
+
+
+ /**
+ * The current color value
+ * @var {x3dom.fields.SFColor} value
+ * @memberof x3dom.nodeTypes.ColorDamper
+ * @initvalue 0,0,0
+ * @range [0,1]
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFColor(ctx, 'value', 0, 0, 0);
+
+ /**
+ * The target color value
+ * @var {x3dom.fields.SFColor} destination
+ * @memberof x3dom.nodeTypes.ColorDamper
+ * @initvalue 0,0,0
+ * @range [0,1]
+ * @field x3d
+ * @instance
+ */
+ this.addField_SFColor(ctx, 'destination', 0, 0, 0);
+
+ this._value0 = new x3dom.fields.SFColor(0, 0, 0);
+ this._value1 = new x3dom.fields.SFColor(0, 0, 0);
+ this._value2 = new x3dom.fields.SFColor(0, 0, 0);
+ this._value3 = new x3dom.fields.SFColor(0, 0, 0);
+ this._value4 = new x3dom.fields.SFColor(0, 0, 0);
+ this._value5 = new x3dom.fields.SFColor(0, 0, 0);
+
+ this.initialize();
+
+ },
+ {
+ fieldChanged: function(fieldName)
+ {
+ if (fieldName === "tolerance")
+ {
+ this._eps = this._vf.tolerance < 0 ? 0.001 : this._vf.tolerance;
+ }
+ else if (fieldName.indexOf("destination") >= 0)
+ {
+ if ( !this._value0.equals(this._vf.destination, this._eps) ) {
+ this._value0 = this._vf.destination;
+
+ if (!this._vf.isActive) {
+ //this._lastTick = 0;
+ this.postMessage('isActive', true);
+ }
+ }
+ }
+ else if (fieldName.indexOf("value") >= 0)
+ {
+ this._value1.setValues(this._vf.value);
+ this._value2.setValues(this._vf.value);
+ this._value3.setValues(this._vf.value);
+ this._value4.setValues(this._vf.value);
+ this._value5.setValues(this._vf.value);
+ this._lastTick = 0;
+
+ this.postMessage('value', this._value5);
+
+ if (!this._vf.isActive) {
+ this._lastTick = 0;
+ this.postMessage('isActive', true);
+ }
+ }
+ },
+
+ initialize: function()
+ {
+ this._value0.setValues(this._vf.initialDestination);
+ this._value1.setValues(this._vf.initialValue);
+ this._value2.setValues(this._vf.initialValue);
+ this._value3.setValues(this._vf.initialValue);
+ this._value4.setValues(this._vf.initialValue);
+ this._value5.setValues(this._vf.initialValue);
+ this._lastTick = 0;
+
+ var active = !this._value0.equals(this._value1, this._eps);
+ if (this._vf.isActive !== active) {
+ this.postMessage('isActive', active);
+ }
+ },
+
+ distance: function(a, b)
+ {
+ var diff = a.subtract(b);
+ return Math.sqrt(diff.r*diff.r + diff.g*diff.g + diff.b*diff.b);
+ },
+
+ // The ColorDamper animates SFColor values not in HSV space
+ // but as proposed in the original PROTO code in RGB space.
+ tick: function(now)
+ {
+ //if (!this._vf.isActive)
+ // return false;
+
+ if (!this._lastTick)
+ {
+ this._lastTick = now;
+ return false;
+ }
+
+ var delta = now - this._lastTick;
+
+ var alpha = Math.exp(-delta / this._vf.tau);
+
+ this._value1 = this._vf.order > 0 && this._vf.tau ?
+ this._value0.add(this._value1.subtract(this._value0).multiply(alpha)) :
+ new x3dom.fields.SFColor(this._value0.r, this._value0.g, this._value0.b);
+
+ this._value2 = this._vf.order > 1 && this._vf.tau ?
+ this._value1.add(this._value2.subtract(this._value1).multiply(alpha)) :
+ new x3dom.fields.SFColor(this._value1.r, this._value1.g, this._value1.b);
+
+ this._value3 = this._vf.order > 2 && this._vf.tau ?
+ this._value2.add(this._value3.subtract(this._value2).multiply(alpha)) :
+ new x3dom.fields.SFColor(this._value2.r, this._value2.g, this._value2.b);
+
+ this._value4 = this._vf.order > 3 && this._vf.tau ?
+ this._value3.add(this._value4.subtract(this._value3).multiply(alpha)) :
+ new x3dom.fields.SFColor(this._value3.r, this._value3.g, this._value3.b);
+
+ this._value5 = this._vf.order > 4 && this._vf.tau ?
+ this._value4.add(this._value5.subtract(this._value4).multiply(alpha)) :
+ new x3dom.fields.SFColor(this._value4.r, this._value4.g, this._value4.b);
+
+ var dist = this.distance(this._value1, this._value0);
+
+ if (this._vf.order > 1)
+ {
+ var dist2 = this.distance(this._value2, this._value1);
+ if (dist2 > dist) { dist = dist2; }
+ }
+ if (this._vf.order > 2)
+ {
+ var dist3 = this.distance(this._value3, this._value2);
+ if (dist3 > dist) { dist = dist3; }
+ }
+ if (this._vf.order > 3)
+ {
+ var dist4 = this.distance(this._value4, this._value3);
+ if (dist4 > dist) { dist = dist4; }
+ }
+ if (this._vf.order > 4)
+ {
+ var dist5 = this.distance(this._value5, this._value4);
+ if (dist5 > dist) { dist = dist5; }
+ }
+
+ if (dist <= this._eps)
+ {
+ this._value1.setValues(this._value0);
+ this._value2.setValues(this._value0);
+ this._value3.setValues(this._value0);
+ this._value4.setValues(this._value0);
+ this._value5.setValues(this._value0);
+
+ this.postMessage('value', this._value0);
+ this.postMessage('isActive', false);
+
+ this._lastTick = 0;
+
+ return false;
+ }
+
+ this.postMessage('value', this._value5);
+
+ this._lastTick = now;
+
+ return true;
+ }
+ }
+ )
+);
+/** @namespace x3dom.nodeTypes */
+/*
+ * X3DOM JavaScript Library
+ * http://www.x3dom.org
+ *
+ * (C)2009 Fraunhofer IGD, Darmstadt, Germany
+ * Dual licensed under the MIT and GPL
+ */
+
+/* ### OrientationChaser ### */
+x3dom.registerNodeType(
+ "OrientationChaser",
+ "Followers",
+ defineClass(x3dom.nodeTypes.X3DChaserNode,
+
+ /**
+ * Constructor for OrientationChaser
+ * @constructs x3dom.nodeTypes.OrientationChaser
+ * @x3d 3.3
+ * @component Followers
+ * @status experimental
+ * @extends x3dom.nodeTypes.X3DChaserNode
+ * @param {Object} [ctx=null] - context object, containing initial settings like namespace
+ * @classdesc The OrientationChaser animates transitions for orientations. If it is routed to a rotation field
+ * of a Transform node that contains an object, whenever the set_destination field receives an orientation, the
+ * OrientationChaser node rotates the object from its current orientation to the newly set orientation.
+ * It creates a smooth transition that ends duration seconds after the last orientation has been received.
+ */
+ function (ctx) {
+ x3dom.nodeTypes.OrientationChaser.superClass.call(this, ctx);
+
+
+ /**
+ * The field initialDestination should be set to the same value than initialValue unless a transition to a
+ * certain orientation is to be created right after the scene is loaded or right after the
+ * OrientationChaser node is created dynamically.
+ * @var {x3dom.fields.SFRotation} initialDestination
+ * @memberof x3dom.nodeTypes.OrientationChaser
+ * @initvalue 0,1,0,0
+ * @field x3d
+ * @instance
+ */
+ this.addField_SFRotation(ctx, 'initialDestination', 0, 1, 0, 0);
+
+ /**
+ * The field initialValue can be used to set the initial orientation of the object.
+ * @var {x3dom.fields.SFRotation} initialValue
+ * @memberof x3dom.nodeTypes.OrientationChaser
+ * @initvalue 0,1,0,0
+ * @field x3d
+ * @instance
+ */
+ this.addField_SFRotation(ctx, 'initialValue', 0, 1, 0, 0);
+
+
+ /**
+ * The current orientation value.
+ * @var {x3dom.fields.SFRotation} value
+ * @memberof x3dom.nodeTypes.OrientationChaser
+ * @initvalue 0,1,0,0
+ * @field x3d
+ * @instance
+ */
+ this.addField_SFRotation(ctx, 'value', 0, 1, 0, 0);
+
+ /**
+ * The target orientation value.
+ * @var {x3dom.fields.SFRotation} destination
+ * @memberof x3dom.nodeTypes.OrientationChaser
+ * @initvalue 0,1,0,0
+ * @field x3d
+ * @instance
+ */
+ this.addField_SFRotation(ctx, 'destination', 0, 1, 0, 0);
+
+ this._numSupports = 30;
+ this._buffer = new x3dom.fields.MFRotation();
+ this._previousValue = new x3dom.fields.Quaternion(0, 1, 0, 0);
+ this._value = new x3dom.fields.Quaternion(0, 1, 0, 0);
+
+ this.initialize();
+
+ },
+ {
+ fieldChanged: function(fieldName)
+ {
+ if (fieldName.indexOf("destination") >= 0)
+ {
+ this.initialize();
+ this.updateBuffer(this._currTime);
+
+ if (!this._vf.isActive) {
+ this.postMessage('isActive', true);
+ }
+ }
+ else if (fieldName.indexOf("value") >= 0)
+ {
+ this.initialize();
+
+ this._previousValue.setValues(this._vf.value);
+ for (var C=1; C<this._buffer.length; C++) {
+ this._buffer[C].setValues(this._vf.value);
+ }
+
+ this.postMessage('value', this._vf.value);
+
+ if (!this._vf.isActive) {
+ this.postMessage('isActive', true);
+ }
+ }
+ },
+
+ /** The following handler code was basically taken from
+ * http://www.hersto.com/X3D/Followers
+ */
+ initialize: function()
+ {
+ if (!this._initDone)
+ {
+ this._initDone = true;
+
+ this._vf.destination = x3dom.fields.Quaternion.copy(this._vf.initialDestination);
+
+ this._buffer.length = this._numSupports;
+
+ this._buffer[0] = x3dom.fields.Quaternion.copy(this._vf.initialDestination);
+ for (var C=1; C<this._buffer.length; C++) {
+ this._buffer[C] = x3dom.fields.Quaternion.copy(this._vf.initialValue);
+ }
+
+ this._previousValue = x3dom.fields.Quaternion.copy(this._vf.initialValue);
+
+ this._stepTime = this._vf.duration / this._numSupports;
+
+ var active = !this._buffer[0].equals(this._buffer[1], this._eps);
+ if (this._vf.isActive !== active) {
+ this.postMessage('isActive', active);
+ }
+ }
+ },
+
+ tick: function(now)
+ {
+ this.initialize();
+ this._currTime = now;
+
+ //if (!this._vf.isActive)
+ // return false;
+
+ if (!this._bufferEndTime)
+ {
+ this._bufferEndTime = now; // first event we received, so we are in the initialization phase.
+
+ this._value = x3dom.fields.Quaternion.copy(this._vf.initialValue);
+
+ this.postMessage('value', this._value);
+
+ return true;
+ }
+
+ var Frac = this.updateBuffer(now);
+ // Frac is a value in 0 <= Frac < 1.
+
+ // now we can calculate the output.
+ // This means we calculate the delta between each entry in _buffer and its previous
+ // entries, calculate the step response of each such step and add it to form the output.
+
+ // The oldest value _buffer[_buffer.length - 1] needs some extra thought, because it has
+ // no previous value. More exactly, we haven't stored a previous value anymore.
+ // However, the step response of that missing previous value has already reached its
+ // destination, so we can - would we have that previous value - use this as a start point
+ // for adding the step responses.
+ // Actually updateBuffer(.) maintains this value in
+
+ var Output = x3dom.fields.Quaternion.copy(this._previousValue);
+
+ var DeltaIn = this._previousValue.inverse().multiply(this._buffer[this._buffer.length - 1]);
+
+ Output = Output.slerp(Output.multiply(DeltaIn), this.stepResponse((this._buffer.length - 1 + Frac) * this._stepTime));
+
+ for (var C=this._buffer.length - 2; C>=0; C--)
+ {
+ DeltaIn = this._buffer[C + 1].inverse().multiply(this._buffer[C]);
+
+ Output = Output.slerp(Output.multiply(DeltaIn), this.stepResponse((C + Frac) * this._stepTime));
+ }
+
+ if ( !Output.equals(this._value, this._eps) ) {
+ Output = Output.normalize(Output);
+ this._value.setValues(Output);
+
+ this.postMessage('value', this._value);
+ }
+ else {
+ this.postMessage('isActive', false);
+ }
+
+ return this._vf.isActive;
+ },
+
+ updateBuffer: function(now)
+ {
+ var Frac = (now - this._bufferEndTime) / this._stepTime;
+ var C;
+ var NumToShift;
+ var Alpha;
+ // is normally < 1. When it has grown to be larger than 1, we have to shift the array because the step response
+ // of the oldest entry has already reached its destination, and it's time for a newer entry.
+ // In the case of a very low frame rate, or a very short _stepTime we may need to shift by more than one entry.
+
+ if (Frac >= 1)
+ {
+ NumToShift = Math.floor(Frac);
+ Frac -= NumToShift;
+
+ if( NumToShift < this._buffer.length)
+ {
+ // normal case
+ this._previousValue = x3dom.fields.Quaternion.copy(this._buffer[this._buffer.length - NumToShift]);
+
+ for (C=this._buffer.length - 1; C>=NumToShift; C--) {
+ this._buffer[C] = x3dom.fields.Quaternion.copy(this._buffer[C - NumToShift]);
+ }
+
+ for (C=0; C<NumToShift; C++)
+ {
+ // Hmm, we have a destination value, but don't know how it has
+ // reached the current state.
+ // Therefore we do a linear interpolation from the latest value in the buffer to destination.
+ Alpha = C / NumToShift;
+
+ this._buffer[C] = this._vf.destination.slerp(this._buffer[NumToShift], Alpha);
+ }
+ }
+ else
+ {
+ // degenerated case:
+ //
+ // We have a _VERY_ low frame rate...
+ // we can only guess how we should fill the array.
+ // Maybe we could write part of a linear interpolation
+ // from this._buffer[0] to destination, that goes from this._bufferEndTime to now
+ // (possibly only the end of the interpolation is to be written),
+ // but if we reach here we are in a very degenerate case...
+ // Thus we just write destination to the buffer.
+
+ this._previousValue = x3dom.fields.Quaternion.copy((NumToShift == this._buffer.length) ?
+ this._buffer[0] : this._vf.destination);
+
+ for (C= 0; C<this._buffer.length; C++) {
+ this._buffer[C] = x3dom.fields.Quaternion.copy(this._vf.destination);
+ }
+ }
+
+ this._bufferEndTime += NumToShift * this._stepTime;
+ }
+
+ return Frac;
+ }
+ }
+ )
+);
+/** @namespace x3dom.nodeTypes */
+/*
+ * X3DOM JavaScript Library
+ * http://www.x3dom.org
+ *
+ * (C)2009 Fraunhofer IGD, Darmstadt, Germany
+ * Dual licensed under the MIT and GPL
+ */
+
+/* ### OrientationDamper ### */
+x3dom.registerNodeType(
+ "OrientationDamper",
+ "Followers",
+ defineClass(x3dom.nodeTypes.X3DDamperNode,
+
+ /**
+ * Constructor for OrientationDamper
+ * @constructs x3dom.nodeTypes.OrientationDamper
+ * @x3d 3.3
+ * @component Followers
+ * @status experimental
+ * @extends x3dom.nodeTypes.X3DDamperNode
+ * @param {Object} [ctx=null] - context object, containing initial settings like namespace
+ * @classdesc The OrientationDamper animates transitions of orientations. If its value is routed to an
+ * orientation field of a Transform node that contains an object, then, whenever the destination field receives
+ * an orientation, the OrientationDamper node rotates the object from its current orientation to the newly set
+ * orientation. It creates a transition that approaches the newly set orientation asymptotically during a time
+ * period of approximately three to four times the value of the field tau depending on the desired accuracy and
+ * the value of order. Through this asymptotic approach of the destination orientation, a very smooth
+ * transition is created.
+ */
+ function (ctx) {
+ x3dom.nodeTypes.OrientationDamper.superClass.call(this, ctx);
+
+
+ /**
+ * The field initialDestination should be set to the same value than initialValue unless a transition to a
+ * certain orientation is to be created right after the scene is loaded or right after the
+ * OrientationDamper node is created dynamically.
+ * @var {x3dom.fields.SFRotation} initialDestination
+ * @memberof x3dom.nodeTypes.OrientationDamper
+ * @initvalue 0,1,0,0
+ * @field x3d
+ * @instance
+ */
+ this.addField_SFRotation(ctx, 'initialDestination', 0, 1, 0, 0);
+
+ /**
+ * The field initialValue can be used to set the initial orientation of the object.
+ * @var {x3dom.fields.SFRotation} initialValue
+ * @memberof x3dom.nodeTypes.OrientationDamper
+ * @initvalue 0,1,0,0
+ * @field x3d
+ * @instance
+ */
+ this.addField_SFRotation(ctx, 'initialValue', 0, 1, 0, 0);
+
+
+ /**
+ * The current orientation value.
+ * @var {x3dom.fields.SFRotation} value
+ * @memberof x3dom.nodeTypes.OrientationDamper
+ * @initvalue 0,1,0,0
+ * @field x3d
+ * @instance
+ */
+ this.addField_SFRotation(ctx, 'value', 0, 1, 0, 0);
+
+ /**
+ * The target orientation value
+ * @var {x3dom.fields.SFRotation} destination
+ * @memberof x3dom.nodeTypes.OrientationDamper
+ * @initvalue 0,1,0,0
+ * @field x3d
+ * @instance
+ */
+ this.addField_SFRotation(ctx, 'destination', 0, 1, 0, 0);
+
+ this._value0 = new x3dom.fields.Quaternion(0, 1, 0, 0);
+ this._value1 = new x3dom.fields.Quaternion(0, 1, 0, 0);
+ this._value2 = new x3dom.fields.Quaternion(0, 1, 0, 0);
+ this._value3 = new x3dom.fields.Quaternion(0, 1, 0, 0);
+ this._value4 = new x3dom.fields.Quaternion(0, 1, 0, 0);
+ this._value5 = new x3dom.fields.Quaternion(0, 1, 0, 0);
+
+ this.initialize();
+
+ },
+ {
+ fieldChanged: function(fieldName)
+ {
+ if (fieldName === "tolerance")
+ {
+ this._eps = this._vf.tolerance < 0 ? 0.001 : this._vf.tolerance;
+ }
+ else if (fieldName.indexOf("destination") >= 0)
+ {
+ if ( !this._value0.equals(this._vf.destination, this._eps) ) {
+ this._value0 = this._vf.destination;
+
+ if (!this._vf.isActive) {
+ //this._lastTick = 0;
+ this.postMessage('isActive', true);
+ }
+ }
+ }
+ else if (fieldName.indexOf("value") >= 0)
+ {
+ this._value1.setValues(this._vf.value);
+ this._value2.setValues(this._vf.value);
+ this._value3.setValues(this._vf.value);
+ this._value4.setValues(this._vf.value);
+ this._value5.setValues(this._vf.value);
+ this._lastTick = 0;
+
+ this.postMessage('value', this._value5);
+
+ if (!this._vf.isActive) {
+ this._lastTick = 0;
+ this.postMessage('isActive', true);
+ }
+ }
+ },
+
+ initialize: function()
+ {
+ this._value0.setValues(this._vf.initialDestination);
+ this._value1.setValues(this._vf.initialValue);
+ this._value2.setValues(this._vf.initialValue);
+ this._value3.setValues(this._vf.initialValue);
+ this._value4.setValues(this._vf.initialValue);
+ this._value5.setValues(this._vf.initialValue);
+ this._lastTick = 0;
+
+ var active = !this._value0.equals(this._value1, this._eps);
+ if (this._vf.isActive !== active) {
+ this.postMessage('isActive', active);
+ }
+ },
+
+ tick: function(now)
+ {
+ //if (!this._vf.isActive)
+ // return false;
+
+ if (!this._lastTick)
+ {
+ this._lastTick = now;
+ return false;
+ }
+
+ var delta = now - this._lastTick;
+
+ var alpha = Math.exp(-delta / this._vf.tau);
+
+ this._value1 = this._vf.order > 0 && this._vf.tau ?
+ this._value0.slerp(this._value1, alpha) :
+ new x3dom.fields.Quaternion(this._value0.x, this._value0.y, this._value0.z, this._value0.w);
+
+ this._value2 = this._vf.order > 1 && this._vf.tau ?
+ this._value1.slerp(this._value2, alpha) :
+ new x3dom.fields.Quaternion(this._value1.x, this._value1.y, this._value1.z, this._value1.w);
+
+ this._value3 = this._vf.order > 2 && this._vf.tau ?
+ this._value2.slerp(this._value3, alpha) :
+ new x3dom.fields.Quaternion(this._value2.x, this._value2.y, this._value2.z, this._value2.w);
+
+ this._value4 = this._vf.order > 3 && this._vf.tau ?
+ this._value3.slerp(this._value4, alpha) :
+ new x3dom.fields.Quaternion(this._value3.x, this._value3.y, this._value3.z, this._value3.w);
+
+ this._value5 = this._vf.order > 4 && this._vf.tau ?
+ this._value4.slerp(this._value5, alpha) :
+ new x3dom.fields.Quaternion(this._value4.x, this._value4.y, this._value4.z, this._value4.w);
+
+ var dist = Math.abs(this._value1.inverse().multiply(this._value0).angle());
+
+ if(this._vf.order > 1)
+ {
+ var dist2 = Math.abs(this._value2.inverse().multiply(this._value1).angle());
+ if (dist2 > dist) { dist = dist2; }
+ }
+ if(this._vf.order > 2)
+ {
+ var dist3 = Math.abs(this._value3.inverse().multiply(this._value2).angle());
+ if (dist3 > dist) { dist = dist3; }
+ }
+ if(this._vf.order > 3)
+ {
+ var dist4 = Math.abs(this._value4.inverse().multiply(this._value3).angle());
+ if (dist4 > dist) { dist = dist4; }
+ }
+ if(this._vf.order > 4)
+ {
+ var dist5 = Math.abs(this._value5.inverse().multiply(this._value4).angle());
+ if (dist5 > dist) { dist = dist5; }
+ }
+
+ if (dist <= this._eps)
+ {
+ this._value1.setValues(this._value0);
+ this._value2.setValues(this._value0);
+ this._value3.setValues(this._value0);
+ this._value4.setValues(this._value0);
+ this._value5.setValues(this._value0);
+
+ this.postMessage('value', this._value0);
+ this.postMessage('isActive', false);
+
+ this._lastTick = 0;
+
+ return false;
+ }
+
+ this.postMessage('value', this._value5);
+
+ this._lastTick = now;
+
+ return true;
+ }
+ }
+ )
+);
+/** @namespace x3dom.nodeTypes */
+/*
+ * X3DOM JavaScript Library
+ * http://www.x3dom.org
+ *
+ * (C)2009 Fraunhofer IGD, Darmstadt, Germany
+ * Dual licensed under the MIT and GPL
+ */
+
+/* ### PositionChaser ### */
+x3dom.registerNodeType(
+ "PositionChaser",
+ "Followers",
+ defineClass(x3dom.nodeTypes.X3DChaserNode,
+
+ /**
+ * Constructor for PositionChaser
+ * @constructs x3dom.nodeTypes.PositionChaser
+ * @x3d 3.3
+ * @component Followers
+ * @status experimental
+ * @extends x3dom.nodeTypes.X3DChaserNode
+ * @param {Object} [ctx=null] - context object, containing initial settings like namespace
+ * @classdesc The PositionChaser animates transitions for 3D vectors. If its value field is routed to a
+ * translation field of a Transform node that contains an object, then, whenever the destination field
+ * receives a 3D position, the PositionChaser node moves the object from its current position to the newly set
+ * position. It creates a smooth transition that ends duration seconds after the last position has been
+ * received.
+ */
+ function (ctx) {
+ x3dom.nodeTypes.PositionChaser.superClass.call(this, ctx);
+
+
+ /**
+ * The field initialDestination should be set to the same value than initialValue unless a transition to a
+ * certain position is to be created right after the scene is loaded or right after the PositionChaser node
+ * is created dynamically.
+ * @var {x3dom.fields.SFVec3f} initialDestination
+ * @memberof x3dom.nodeTypes.PositionChaser
+ * @initvalue 0,0,0
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFVec3f(ctx, 'initialDestination', 0, 0, 0);
+
+ /**
+ * The field initialValue can be used to set the initial position of the object.
+ * @var {x3dom.fields.SFVec3f} initialValue
+ * @memberof x3dom.nodeTypes.PositionChaser
+ * @initvalue 0,0,0
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFVec3f(ctx, 'initialValue', 0, 0, 0);
+
+
+ /**
+ * The current orientation value.
+ * @var {x3dom.fields.SFVec3f} value
+ * @memberof x3dom.nodeTypes.PositionChaser
+ * @initvalue 0,0,0
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFVec3f(ctx, 'value', 0, 0, 0);
+
+ /**
+ * The target orientation value.
+ * @var {x3dom.fields.SFVec3f} destination
+ * @memberof x3dom.nodeTypes.PositionChaser
+ * @initvalue 0,0,0
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFVec3f(ctx, 'destination', 0, 0, 0);
+
+ this._buffer = new x3dom.fields.MFVec3f();
+ this._previousValue = new x3dom.fields.SFVec3f(0, 0, 0);
+ this._value = new x3dom.fields.SFVec3f(0, 0, 0);
+
+ this.initialize();
+
+ },
+ {
+ fieldChanged: function(fieldName)
+ {
+ if (fieldName.indexOf("destination") >= 0)
+ {
+ this.initialize();
+ this.updateBuffer(this._currTime);
+
+ if (!this._vf.isActive) {
+ this.postMessage('isActive', true);
+ }
+ }
+ else if (fieldName.indexOf("value") >= 0)
+ {
+ this.initialize();
+
+ this._previousValue.setValues(this._vf.value);
+ for (var C=1; C<this._buffer.length; C++) {
+ this._buffer[C].setValues(this._vf.value);
+ }
+
+ this.postMessage('value', this._vf.value);
+
+ if (!this._vf.isActive) {
+ this.postMessage('isActive', true);
+ }
+ }
+ },
+
+ /** The following handler code was basically taken from
+ * http://www.hersto.com/X3D/Followers
+ */
+ initialize: function()
+ {
+ if (!this._initDone)
+ {
+ this._initDone = true;
+
+ this._vf.destination = x3dom.fields.SFVec3f.copy(this._vf.initialDestination);
+
+ this._buffer.length = this._numSupports;
+
+ this._buffer[0] = x3dom.fields.SFVec3f.copy(this._vf.initialDestination);
+ for (var C=1; C<this._buffer.length; C++) {
+ this._buffer[C] = x3dom.fields.SFVec3f.copy(this._vf.initialValue);
+ }
+
+ this._previousValue = x3dom.fields.SFVec3f.copy(this._vf.initialValue);
+
+ this._stepTime = this._vf.duration / this._numSupports;
+
+ var active = !this._buffer[0].equals(this._buffer[1], this._eps);
+ if (this._vf.isActive !== active) {
+ this.postMessage('isActive', active);
+ }
+ }
+ },
+
+ tick: function(now)
+ {
+ this.initialize();
+ this._currTime = now;
+
+ //if (!this._vf.isActive)
+ // return false;
+
+ if (!this._bufferEndTime)
+ {
+ this._bufferEndTime = now; // first event we received, so we are in the initialization phase.
+
+ this._value = x3dom.fields.SFVec3f.copy(this._vf.initialValue);
+
+ this.postMessage('value', this._value);
+
+ return true;
+ }
+
+ var Frac = this.updateBuffer(now);
+ // Frac is a value in 0 <= Frac < 1.
+
+ // now we can calculate the output.
+ // This means we calculate the delta between each entry in _buffer and its previous
+ // entries, calculate the step response of each such step and add it to form the output.
+
+ // The oldest value _buffer[_buffer.length - 1] needs some extra thought, because it has
+ // no previous value. More exactly, we haven't stored a previous value anymore.
+ // However, the step response of that missing previous value has already reached its
+ // destination, so we can - would we have that previous value - use this as a start point
+ // for adding the step responses.
+ // Actually updateBuffer(.) maintains this value in
+
+ var Output = x3dom.fields.SFVec3f.copy(this._previousValue);
+
+ var DeltaIn = this._buffer[this._buffer.length - 1].subtract(this._previousValue);
+
+ var DeltaOut = DeltaIn.multiply(this.stepResponse((this._buffer.length - 1 + Frac) * this._stepTime));
+
+ Output = Output.add(DeltaOut);
+
+ for (var C=this._buffer.length - 2; C>=0; C--)
+ {
+ DeltaIn = this._buffer[C].subtract(this._buffer[C + 1]);
+
+ DeltaOut = DeltaIn.multiply(this.stepResponse((C + Frac) * this._stepTime));
+
+ Output = Output.add(DeltaOut);
+ }
+
+ if ( !Output.equals(this._value, this._eps) ) {
+ this._value.setValues(Output);
+
+ this.postMessage('value', this._value);
+ }
+ else {
+ this.postMessage('isActive', false);
+ }
+
+ return this._vf.isActive;
+ },
+
+ updateBuffer: function(now)
+ {
+ var Frac = (now - this._bufferEndTime) / this._stepTime;
+ var C;
+ var NumToShift;
+ var Alpha;
+ // is normally < 1. When it has grown to be larger than 1, we have to shift the array because the step response
+ // of the oldest entry has already reached its destination, and it's time for a newer entry.
+ // In the case of a very low frame rate, or a very short _stepTime we may need to shift by more than one entry.
+
+ if (Frac >= 1)
+ {
+ NumToShift = Math.floor(Frac);
+ Frac -= NumToShift;
+
+ if( NumToShift < this._buffer.length)
+ {
+ // normal case
+ this._previousValue = x3dom.fields.SFVec3f.copy(this._buffer[this._buffer.length - NumToShift]);
+
+ for (C=this._buffer.length - 1; C>=NumToShift; C--) {
+ this._buffer[C] = x3dom.fields.SFVec3f.copy(this._buffer[C - NumToShift]);
+ }
+
+ for (C=0; C<NumToShift; C++)
+ {
+ // Hmm, we have a destination value, but don't know how it has
+ // reached the current state.
+ // Therefore we do a linear interpolation from the latest value in the buffer to destination.
+ Alpha = C / NumToShift;
+
+ this._buffer[C] = this._buffer[NumToShift].multiply(Alpha).add(this._vf.destination.multiply((1 - Alpha)));
+ }
+ }
+ else
+ {
+ // degenerated case:
+ //
+ // We have a _VERY_ low frame rate...
+ // we can only guess how we should fill the array.
+ // Maybe we could write part of a linear interpolation
+ // from this._buffer[0] to destination, that goes from this._bufferEndTime to now
+ // (possibly only the end of the interpolation is to be written),
+ // but if we reach here we are in a very degenerate case...
+ // Thus we just write destination to the buffer.
+
+ this._previousValue = x3dom.fields.SFVec3f.copy((NumToShift == this._buffer.length) ?
+ this._buffer[0] : this._vf.destination);
+
+ for (C= 0; C<this._buffer.length; C++) {
+ this._buffer[C] = x3dom.fields.SFVec3f.copy(this._vf.destination);
+ }
+ }
+
+ this._bufferEndTime += NumToShift * this._stepTime;
+ }
+
+ return Frac;
+ }
+ }
+ )
+);
+/** @namespace x3dom.nodeTypes */
+/*
+ * X3DOM JavaScript Library
+ * http://www.x3dom.org
+ *
+ * (C)2009 Fraunhofer IGD, Darmstadt, Germany
+ * Dual licensed under the MIT and GPL
+ */
+
+/* ### PositionChaser2D ### */
+x3dom.registerNodeType(
+ "PositionChaser2D",
+ "Followers",
+ defineClass(x3dom.nodeTypes.X3DChaserNode,
+
+ /**
+ * Constructor for PositionChaser2D
+ * @constructs x3dom.nodeTypes.PositionChaser2D
+ * @x3d 3.3
+ * @component Followers
+ * @status experimental
+ * @extends x3dom.nodeTypes.X3DChaserNode
+ * @param {Object} [ctx=null] - context object, containing initial settings like namespace
+ * @classdesc The PositionChaser2D animates transitions for 2D vectors. Whenever its destination field receives
+ * a 2D vector it creates a transition from its current 2D vector value to the newly set value. It creates a
+ * smooth transition that ends duration seconds after the last 2D vector has been received.
+ */
+ function (ctx) {
+ x3dom.nodeTypes.PositionChaser2D.superClass.call(this, ctx);
+
+
+ /**
+ * The field initialDestination should be set to the same value than initialValue unless a transition to a
+ * certain 2D vector value is to be created right after the scene is loaded or right after the
+ * PositionChaser2D node is created dynamically.
+ * @var {x3dom.fields.SFVec2f} initialDestination
+ * @memberof x3dom.nodeTypes.PositionChaser2D
+ * @initvalue 0,0
+ * @field x3d
+ * @instance
+ */
+ this.addField_SFVec2f(ctx, 'initialDestination', 0, 0);
+
+ /**
+ * The field initialValue can be used to set the initial initial value.
+ * @var {x3dom.fields.SFVec2f} initialValue
+ * @memberof x3dom.nodeTypes.PositionChaser2D
+ * @initvalue 0,0
+ * @field x3d
+ * @instance
+ */
+ this.addField_SFVec2f(ctx, 'initialValue', 0, 0);
+
+
+ /**
+ * The current 2D position.
+ * @var {x3dom.fields.SFVec2f} value
+ * @memberof x3dom.nodeTypes.PositionChaser2D
+ * @initvalue 0,0
+ * @field x3d
+ * @instance
+ */
+ this.addField_SFVec2f(ctx, 'value', 0, 0);
+
+ /**
+ * The target 2D position.
+ * @var {x3dom.fields.SFVec2f} destination
+ * @memberof x3dom.nodeTypes.PositionChaser2D
+ * @initvalue 0,0
+ * @field x3d
+ * @instance
+ */
+ this.addField_SFVec2f(ctx, 'destination', 0, 0);
+
+ this._buffer = new x3dom.fields.MFVec2f();
+ this._previousValue = new x3dom.fields.SFVec2f(0, 0);
+ this._value = new x3dom.fields.SFVec2f(0, 0);
+
+ this.initialize();
+
+ },
+ {
+ fieldChanged: function(fieldName)
+ {
+ if (fieldName.indexOf("destination") >= 0)
+ {
+ this.initialize();
+ this.updateBuffer(this._currTime);
+
+ if (!this._vf.isActive) {
+ this.postMessage('isActive', true);
+ }
+ }
+ else if (fieldName.indexOf("value") >= 0)
+ {
+ this.initialize();
+
+ this._previousValue.setValues(this._vf.value);
+ for (var C=1; C<this._buffer.length; C++) {
+ this._buffer[C].setValues(this._vf.value);
+ }
+
+ this.postMessage('value', this._vf.value);
+
+ if (!this._vf.isActive) {
+ this.postMessage('isActive', true);
+ }
+ }
+ },
+
+ /** The following handler code is copy & paste from PositionChaser
+ */
+ initialize: function()
+ {
+ if (!this._initDone)
+ {
+ this._initDone = true;
+
+ this._vf.destination = x3dom.fields.SFVec2f.copy(this._vf.initialDestination);
+
+ this._buffer.length = this._numSupports;
+
+ this._buffer[0] = x3dom.fields.SFVec2f.copy(this._vf.initialDestination);
+ for (var C=1; C<this._buffer.length; C++) {
+ this._buffer[C] = x3dom.fields.SFVec2f.copy(this._vf.initialValue);
+ }
+
+ this._previousValue = x3dom.fields.SFVec2f.copy(this._vf.initialValue);
+
+ this._stepTime = this._vf.duration / this._numSupports;
+
+ var active = !this._buffer[0].equals(this._buffer[1], this._eps);
+ if (this._vf.isActive !== active) {
+ this.postMessage('isActive', active);
+ }
+ }
+ },
+
+ tick: function(now)
+ {
+ this.initialize();
+ this._currTime = now;
+
+ //if (!this._vf.isActive)
+ // return false;
+
+ if (!this._bufferEndTime)
+ {
+ this._bufferEndTime = now;
+
+ this._value = x3dom.fields.SFVec2f.copy(this._vf.initialValue);
+
+ this.postMessage('value', this._value);
+
+ return true;
+ }
+
+ var Frac = this.updateBuffer(now);
+
+ var Output = x3dom.fields.SFVec2f.copy(this._previousValue);
+
+ var DeltaIn = this._buffer[this._buffer.length - 1].subtract(this._previousValue);
+
+ var DeltaOut = DeltaIn.multiply(this.stepResponse((this._buffer.length - 1 + Frac) * this._stepTime));
+
+ Output = Output.add(DeltaOut);
+
+ for (var C=this._buffer.length - 2; C>=0; C--)
+ {
+ DeltaIn = this._buffer[C].subtract(this._buffer[C + 1]);
+
+ DeltaOut = DeltaIn.multiply(this.stepResponse((C + Frac) * this._stepTime));
+
+ Output = Output.add(DeltaOut);
+ }
+
+ if ( !Output.equals(this._value, this._eps) ) {
+ this._value.setValues(Output);
+
+ this.postMessage('value', this._value);
+ }
+ else {
+ this.postMessage('isActive', false);
+ }
+
+ return this._vf.isActive;
+ },
+
+ updateBuffer: function(now)
+ {
+ var Frac = (now - this._bufferEndTime) / this._stepTime;
+ var C;
+ var NumToShift;
+ var Alpha;
+
+ if (Frac >= 1)
+ {
+ NumToShift = Math.floor(Frac);
+ Frac -= NumToShift;
+
+ if( NumToShift < this._buffer.length)
+ {
+ this._previousValue = x3dom.fields.SFVec2f.copy(this._buffer[this._buffer.length - NumToShift]);
+
+ for (C=this._buffer.length - 1; C>=NumToShift; C--) {
+ this._buffer[C]= x3dom.fields.SFVec2f.copy(this._buffer[C - NumToShift]);
+ }
+
+ for (C=0; C<NumToShift; C++)
+ {
+ Alpha = C / NumToShift;
+
+ this._buffer[C] = this._buffer[NumToShift].multiply(Alpha).add(this._vf.destination.multiply((1 - Alpha)));
+ }
+ }
+ else
+ {
+ this._previousValue = x3dom.fields.SFVec2f.copy((NumToShift == this._buffer.length) ?
+ this._buffer[0] : this._vf.destination);
+
+ for (C= 0; C<this._buffer.length; C++) {
+ this._buffer[C] = x3dom.fields.SFVec2f.copy(this._vf.destination);
+ }
+ }
+
+ this._bufferEndTime += NumToShift * this._stepTime;
+ }
+
+ return Frac;
+ }
+ }
+ )
+);
+/** @namespace x3dom.nodeTypes */
+/*
+ * X3DOM JavaScript Library
+ * http://www.x3dom.org
+ *
+ * (C)2009 Fraunhofer IGD, Darmstadt, Germany
+ * Dual licensed under the MIT and GPL
+ */
+
+/* ### PositionDamper ### */
+x3dom.registerNodeType(
+ "PositionDamper",
+ "Followers",
+ defineClass(x3dom.nodeTypes.X3DDamperNode,
+
+ /**
+ * Constructor for PositionDamper
+ * @constructs x3dom.nodeTypes.PositionDamper
+ * @x3d 3.3
+ * @component Followers
+ * @status experimental
+ * @extends x3dom.nodeTypes.X3DDamperNode
+ * @param {Object} [ctx=null] - context object, containing initial settings like namespace
+ * @classdesc The PositionDamper animates transitions for 3D vectors. If its value field is routed to a
+ * translation field of a Transform node that contains an object, then, whenever the destination field receives
+ * a 3D position, the PositionDamper node moves the object from its current position to the newly set position.
+ */
+ function (ctx) {
+ x3dom.nodeTypes.PositionDamper.superClass.call(this, ctx);
+
+
+ /**
+ * The field initialDestination should be set to the same value than initialvalue unless a transition to a
+ * certain position is to be created right after the scene is loaded or right after the PositionDamper node
+ * is created dynamically.
+ * @var {x3dom.fields.SFVec3f} initialDestination
+ * @memberof x3dom.nodeTypes.PositionDamper
+ * @initvalue 0,0,0
+ * @field x3d
+ * @instance
+ */
+ this.addField_SFVec3f(ctx, 'initialDestination', 0, 0, 0);
+
+ /**
+ * The field initialValue can be used to set the initial position of the object.
+ * @var {x3dom.fields.SFVec3f} initialValue
+ * @memberof x3dom.nodeTypes.PositionDamper
+ * @initvalue 0,0,0
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFVec3f(ctx, 'initialValue', 0, 0, 0);
+
+
+ /**
+ * The current position value.
+ * @var {x3dom.fields.SFVec3f} value
+ * @memberof x3dom.nodeTypes.PositionDamper
+ * @initvalue 0,0,0
+ * @field x3d
+ * @instance
+ */
+ this.addField_SFVec3f(ctx, 'value', 0, 0, 0);
+
+ /**
+ * The target position value.
+ * @var {x3dom.fields.SFVec3f} destination
+ * @memberof x3dom.nodeTypes.PositionDamper
+ * @initvalue 0,0,0
+ * @field x3d
+ * @instance
+ */
+ this.addField_SFVec3f(ctx, 'destination', 0, 0, 0);
+
+ this._value0 = new x3dom.fields.SFVec3f(0, 0, 0);
+ this._value1 = new x3dom.fields.SFVec3f(0, 0, 0);
+ this._value2 = new x3dom.fields.SFVec3f(0, 0, 0);
+ this._value3 = new x3dom.fields.SFVec3f(0, 0, 0);
+ this._value4 = new x3dom.fields.SFVec3f(0, 0, 0);
+ this._value5 = new x3dom.fields.SFVec3f(0, 0, 0);
+
+ this.initialize();
+
+ },
+ {
+ fieldChanged: function(fieldName)
+ {
+ if (fieldName === "tolerance")
+ {
+ this._eps = this._vf.tolerance < 0 ? 0.001 : this._vf.tolerance;
+ }
+ else if (fieldName.indexOf("destination") >= 0)
+ {
+ if ( !this._value0.equals(this._vf.destination, this._eps) ) {
+ this._value0 = this._vf.destination;
+
+ if (!this._vf.isActive) {
+ //this._lastTick = 0;
+ this.postMessage('isActive', true);
+ }
+ }
+ }
+ else if (fieldName.indexOf("value") >= 0)
+ {
+ this._value1.setValues(this._vf.value);
+ this._value2.setValues(this._vf.value);
+ this._value3.setValues(this._vf.value);
+ this._value4.setValues(this._vf.value);
+ this._value5.setValues(this._vf.value);
+ this._lastTick = 0;
+
+ this.postMessage('value', this._value5);
+
+ if (!this._vf.isActive) {
+ this._lastTick = 0;
+ this.postMessage('isActive', true);
+ }
+ }
+ },
+
+ initialize: function()
+ {
+ this._value0.setValues(this._vf.initialDestination);
+ this._value1.setValues(this._vf.initialValue);
+ this._value2.setValues(this._vf.initialValue);
+ this._value3.setValues(this._vf.initialValue);
+ this._value4.setValues(this._vf.initialValue);
+ this._value5.setValues(this._vf.initialValue);
+ this._lastTick = 0;
+
+ var active = !this._value0.equals(this._value1, this._eps);
+ if (this._vf.isActive !== active) {
+ this.postMessage('isActive', active);
+ }
+ },
+
+ tick: function(now)
+ {
+ //if (!this._vf.isActive)
+ // return false;
+
+ if (!this._lastTick)
+ {
+ this._lastTick = now;
+ return false;
+ }
+
+ var delta = now - this._lastTick;
+
+ var alpha = Math.exp(-delta / this._vf.tau);
+
+ this._value1 = this._vf.order > 0 && this._vf.tau ?
+ this._value0.add(this._value1.subtract(this._value0).multiply(alpha)) :
+ new x3dom.fields.SFVec3f(this._value0.x, this._value0.y, this._value0.z);
+
+ this._value2 = this._vf.order > 1 && this._vf.tau ?
+ this._value1.add(this._value2.subtract(this._value1).multiply(alpha)) :
+ new x3dom.fields.SFVec3f(this._value1.x, this._value1.y, this._value1.z);
+
+ this._value3 = this._vf.order > 2 && this._vf.tau ?
+ this._value2.add(this._value3.subtract(this._value2).multiply(alpha)) :
+ new x3dom.fields.SFVec3f(this._value2.x, this._value2.y, this._value2.z);
+
+ this._value4 = this._vf.order > 3 && this._vf.tau ?
+ this._value3.add(this._value4.subtract(this._value3).multiply(alpha)) :
+ new x3dom.fields.SFVec3f(this._value3.x, this._value3.y, this._value3.z);
+
+ this._value5 = this._vf.order > 4 && this._vf.tau ?
+ this._value4.add(this._value5.subtract(this._value4).multiply(alpha)) :
+ new x3dom.fields.SFVec3f(this._value4.x, this._value4.y, this._value4.z);
+
+ var dist = this._value1.subtract(this._value0).length();
+
+ if (this._vf.order > 1)
+ {
+ var dist2 = this._value2.subtract(this._value1).length();
+ if (dist2 > dist) {dist = dist2;}
+ }
+ if (this._vf.order > 2)
+ {
+ var dist3 = this._value3.subtract(this._value2).length();
+ if (dist3 > dist) {dist = dist3;}
+ }
+ if (this._vf.order > 3)
+ {
+ var dist4 = this._value4.subtract(this._value3).length();
+ if (dist4 > dist) {dist = dist4;}
+ }
+ if (this._vf.order > 4)
+ {
+ var dist5 = this._value5.subtract(this._value4).length();
+ if (dist5 > dist) {dist = dist5;}
+ }
+
+ if (dist <= this._eps)
+ {
+ this._value1.setValues(this._value0);
+ this._value2.setValues(this._value0);
+ this._value3.setValues(this._value0);
+ this._value4.setValues(this._value0);
+ this._value5.setValues(this._value0);
+
+ this.postMessage('value', this._value0);
+ this.postMessage('isActive', false);
+
+ this._lastTick = 0;
+
+ return false;
+ }
+
+ this.postMessage('value', this._value5);
+
+ this._lastTick = now;
+
+ return true;
+ }
+ }
+ )
+);
+/** @namespace x3dom.nodeTypes */
+/*
+ * X3DOM JavaScript Library
+ * http://www.x3dom.org
+ *
+ * (C)2009 Fraunhofer IGD, Darmstadt, Germany
+ * Dual licensed under the MIT and GPL
+ */
+
+/* ### PositionDamper2D ### */
+x3dom.registerNodeType(
+ "PositionDamper2D",
+ "Followers",
+ defineClass(x3dom.nodeTypes.X3DDamperNode,
+
+ /**
+ * Constructor for PositionDamper2D
+ * @constructs x3dom.nodeTypes.PositionDamper2D
+ * @x3d 3.3
+ * @component Followers
+ * @status experimental
+ * @extends x3dom.nodeTypes.X3DDamperNode
+ * @param {Object} [ctx=null] - context object, containing initial settings like namespace
+ * @classdesc The PositionDamper2D animates transitions for 2D vectors. Whenever the destination field receives
+ * a 2D vector, it creates a transition from its current 2D vector value to the newly set value.
+ */
+ function (ctx) {
+ x3dom.nodeTypes.PositionDamper2D.superClass.call(this, ctx);
+
+
+ /**
+ * The field initialDestination should be set to the same value than initialValue unless a transition to a
+ * certain 2D vector value is to be created right after the scene is loaded or right after the
+ * PositinChaser2D node is created dynamically.
+ * @var {x3dom.fields.SFVec2f} initialDestination
+ * @memberof x3dom.nodeTypes.PositionDamper2D
+ * @initvalue 0,0
+ * @field x3d
+ * @instance
+ */
+ this.addField_SFVec2f(ctx, 'initialDestination', 0, 0);
+
+ /**
+ * The field initialValue can be used to set the initial initial value.
+ * @var {x3dom.fields.SFVec2f} initialValue
+ * @memberof x3dom.nodeTypes.PositionDamper2D
+ * @initvalue 0,0
+ * @field x3d
+ * @instance
+ */
+ this.addField_SFVec2f(ctx, 'initialValue', 0, 0);
+
+
+ /**
+ * The current 2D position value.
+ * @var {x3dom.fields.SFVec2f} value
+ * @memberof x3dom.nodeTypes.PositionDamper2D
+ * @initvalue 0,0
+ * @field x3d
+ * @instance
+ */
+ this.addField_SFVec2f(ctx, 'value', 0, 0);
+
+ /**
+ * The target 2D position value.
+ * @var {x3dom.fields.SFVec2f} destination
+ * @memberof x3dom.nodeTypes.PositionDamper2D
+ * @initvalue 0,0
+ * @field x3d
+ * @instance
+ */
+ this.addField_SFVec2f(ctx, 'destination', 0, 0);
+
+ this._value0 = new x3dom.fields.SFVec2f(0, 0);
+ this._value1 = new x3dom.fields.SFVec2f(0, 0);
+ this._value2 = new x3dom.fields.SFVec2f(0, 0);
+ this._value3 = new x3dom.fields.SFVec2f(0, 0);
+ this._value4 = new x3dom.fields.SFVec2f(0, 0);
+ this._value5 = new x3dom.fields.SFVec2f(0, 0);
+
+ this.initialize();
+
+ },
+ {
+ fieldChanged: function(fieldName)
+ {
+ if (fieldName === "tolerance")
+ {
+ this._eps = this._vf.tolerance < 0 ? 0.001 : this._vf.tolerance;
+ }
+ else if (fieldName.indexOf("destination") >= 0)
+ {
+ if ( !this._value0.equals(this._vf.destination, this._eps) ) {
+ this._value0 = this._vf.destination;
+
+ if (!this._vf.isActive) {
+ //this._lastTick = 0;
+ this.postMessage('isActive', true);
+ }
+ }
+ }
+ else if (fieldName.indexOf("value") >= 0)
+ {
+ this._value1.setValues(this._vf.value);
+ this._value2.setValues(this._vf.value);
+ this._value3.setValues(this._vf.value);
+ this._value4.setValues(this._vf.value);
+ this._value5.setValues(this._vf.value);
+ this._lastTick = 0;
+
+ this.postMessage('value', this._value5);
+
+ if (!this._vf.isActive) {
+ this._lastTick = 0;
+ this.postMessage('isActive', true);
+ }
+ }
+ },
+
+ initialize: function()
+ {
+ this._value0.setValues(this._vf.initialDestination);
+ this._value1.setValues(this._vf.initialValue);
+ this._value2.setValues(this._vf.initialValue);
+ this._value3.setValues(this._vf.initialValue);
+ this._value4.setValues(this._vf.initialValue);
+ this._value5.setValues(this._vf.initialValue);
+ this._lastTick = 0;
+
+ var active = !this._value0.equals(this._value1, this._eps);
+ if (this._vf.isActive !== active) {
+ this.postMessage('isActive', active);
+ }
+ },
+
+ tick: function(now)
+ {
+ //if (!this._vf.isActive)
+ // return false;
+
+ if (!this._lastTick)
+ {
+ this._lastTick = now;
+ return false;
+ }
+
+ var delta = now - this._lastTick;
+
+ var alpha = Math.exp(-delta / this._vf.tau);
+
+ this._value1 = this._vf.order > 0 && this._vf.tau ?
+ this._value0.add(this._value1.subtract(this._value0).multiply(alpha)) :
+ new x3dom.fields.SFVec2f(this._value0.x, this._value0.y, this._value0.z);
+
+ this._value2 = this._vf.order > 1 && this._vf.tau ?
+ this._value1.add(this._value2.subtract(this._value1).multiply(alpha)) :
+ new x3dom.fields.SFVec2f(this._value1.x, this._value1.y, this._value1.z);
+
+ this._value3 = this._vf.order > 2 && this._vf.tau ?
+ this._value2.add(this._value3.subtract(this._value2).multiply(alpha)) :
+ new x3dom.fields.SFVec2f(this._value2.x, this._value2.y, this._value2.z);
+
+ this._value4 = this._vf.order > 3 && this._vf.tau ?
+ this._value3.add(this._value4.subtract(this._value3).multiply(alpha)) :
+ new x3dom.fields.SFVec2f(this._value3.x, this._value3.y, this._value3.z);
+
+ this._value5 = this._vf.order > 4 && this._vf.tau ?
+ this._value4.add(this._value5.subtract(this._value4).multiply(alpha)) :
+ new x3dom.fields.SFVec2f(this._value4.x, this._value4.y, this._value4.z);
+
+ var dist = this._value1.subtract(this._value0).length();
+
+ if (this._vf.order > 1)
+ {
+ var dist2 = this._value2.subtract(this._value1).length();
+ if (dist2 > dist) {dist = dist2;}
+ }
+ if (this._vf.order > 2)
+ {
+ var dist3 = this._value3.subtract(this._value2).length();
+ if (dist3 > dist) {dist = dist3;}
+ }
+ if (this._vf.order > 3)
+ {
+ var dist4 = this._value4.subtract(this._value3).length();
+ if (dist4 > dist) {dist = dist4;}
+ }
+ if (this._vf.order > 4)
+ {
+ var dist5 = this._value5.subtract(this._value4).length();
+ if (dist5 > dist) {dist = dist5;}
+ }
+
+ if (dist <= this._eps)
+ {
+ this._value1.setValues(this._value0);
+ this._value2.setValues(this._value0);
+ this._value3.setValues(this._value0);
+ this._value4.setValues(this._value0);
+ this._value5.setValues(this._value0);
+
+ this.postMessage('value', this._value0);
+ this.postMessage('isActive', false);
+
+ this._lastTick = 0;
+
+ return false;
+ }
+
+ this.postMessage('value', this._value5);
+
+ this._lastTick = now;
+
+ return true;
+ }
+ }
+ )
+);
+/** @namespace x3dom.nodeTypes */
+/*
+ * X3DOM JavaScript Library
+ * http://www.x3dom.org
+ *
+ * (C)2009 Fraunhofer IGD, Darmstadt, Germany
+ * Dual licensed under the MIT and GPL
+ */
+
+/* ### ScalarChaser ### */
+x3dom.registerNodeType(
+ "ScalarChaser",
+ "Followers",
+ defineClass(x3dom.nodeTypes.X3DChaserNode,
+
+ /**
+ * Constructor for ScalarChaser
+ * @constructs x3dom.nodeTypes.ScalarChaser
+ * @x3d 3.3
+ * @component Followers
+ * @status experimental
+ * @extends x3dom.nodeTypes.X3DChaserNode
+ * @param {Object} [ctx=null] - context object, containing initial settings like namespace
+ * @classdesc The ScalarChaser animates transitions for single float values. Whenever the destination field
+ * receives a floating point number, it creates a transition from its current value to the newly set number.
+ * It creates a smooth transition that ends duration seconds after the last number has been received.
+ */
+ function (ctx) {
+ x3dom.nodeTypes.ScalarChaser.superClass.call(this, ctx);
+
+
+ /**
+ * The field initialDestination should be set to the same value than initialValue unless a transition to a
+ * certain value is to be created right after the scene is loaded or right after the ScalarChaser node is
+ * created dynamically.
+ * @var {x3dom.fields.SFFloat} initialDestination
+ * @memberof x3dom.nodeTypes.ScalarChaser
+ * @initvalue 0
+ * @field x3d
+ * @instance
+ */
+ this.addField_SFFloat(ctx, 'initialDestination', 0);
+
+ /**
+ * The field initialValue can be used to set the initial initial value.
+ * @var {x3dom.fields.SFFloat} initialValue
+ * @memberof x3dom.nodeTypes.ScalarChaser
+ * @initvalue 0
+ * @field x3d
+ * @instance
+ */
+ this.addField_SFFloat(ctx, 'initialValue', 0);
+
+
+ /**
+ * The current value.
+ * @var {x3dom.fields.SFFloat} value
+ * @memberof x3dom.nodeTypes.ScalarChaser
+ * @initvalue 0
+ * @field x3d
+ * @instance
+ */
+ this.addField_SFFloat(ctx, 'value', 0);
+
+ /**
+ * The target value.
+ * @var {x3dom.fields.SFFloat} destination
+ * @memberof x3dom.nodeTypes.ScalarChaser
+ * @initvalue 0
+ * @field x3d
+ * @instance
+ */
+ this.addField_SFFloat(ctx, 'destination', 0);
+
+ this._buffer = [];
+ this._previousValue = 0;
+ this._value = 0;
+
+ this.initialize();
+
+ },
+ {
+ fieldChanged: function(fieldName)
+ {
+ if (fieldName.indexOf("destination") >= 0)
+ {
+ this.initialize();
+ this.updateBuffer(this._currTime);
+
+ if (!this._vf.isActive) {
+ this.postMessage('isActive', true);
+ }
+ }
+ else if (fieldName.indexOf("value") >= 0)
+ {
+ this.initialize();
+
+ this._previousValue = this._vf.value;
+ for (var C=1; C<this._buffer.length; C++) {
+ this._buffer[C] = this._vf.value;
+ }
+
+ this.postMessage('value', this._vf.value);
+
+ if (!this._vf.isActive) {
+ this.postMessage('isActive', true);
+ }
+ }
+ },
+
+ initialize: function()
+ {
+ if (!this._initDone)
+ {
+ this._initDone = true;
+
+ this._vf.destination = this._vf.initialDestination;
+
+ this._buffer.length = this._numSupports;
+
+ this._buffer[0] = this._vf.initialDestination;
+ for (var C=1; C<this._buffer.length; C++) {
+ this._buffer[C] = this._vf.initialValue;
+ }
+
+ this._previousValue = this._vf.initialValue;
+
+ this._stepTime = this._vf.duration / this._numSupports;
+
+ var active = (Math.abs(this._buffer[0] - this._buffer[1]) > this._eps);
+ if (this._vf.isActive !== active) {
+ this.postMessage('isActive', active);
+ }
+ }
+ },
+
+ tick: function(now)
+ {
+ this.initialize();
+ this._currTime = now;
+
+ //if (!this._vf.isActive)
+ // return false;
+
+ if (!this._bufferEndTime)
+ {
+ this._bufferEndTime = now;
+
+ this._value = this._vf.initialValue;
+
+ this.postMessage('value', this._value);
+
+ return true;
+ }
+
+ var Frac = this.updateBuffer(now);
+
+ var Output = this._previousValue;
+
+ var DeltaIn = this._buffer[this._buffer.length - 1] - this._previousValue;
+
+ var DeltaOut = DeltaIn * (this.stepResponse((this._buffer.length - 1 + Frac) * this._stepTime));
+
+ Output = Output + DeltaOut;
+
+ for (var C=this._buffer.length - 2; C>=0; C--)
+ {
+ DeltaIn = this._buffer[C] - this._buffer[C + 1];
+
+ DeltaOut = DeltaIn * (this.stepResponse((C + Frac) * this._stepTime));
+
+ Output = Output + DeltaOut;
+ }
+
+ if (Math.abs(Output - this._value) > this._eps) {
+ this._value = Output;
+
+ this.postMessage('value', this._value);
+ }
+ else {
+ this.postMessage('isActive', false);
+ }
+
+ return this._vf.isActive;
+ },
+
+ updateBuffer: function(now)
+ {
+ var Frac = (now - this._bufferEndTime) / this._stepTime;
+ var C;
+ var NumToShift;
+ var Alpha;
+
+ if (Frac >= 1)
+ {
+ NumToShift = Math.floor(Frac);
+ Frac -= NumToShift;
+
+ if (NumToShift < this._buffer.length)
+ {
+ this._previousValue = this._buffer[this._buffer.length - NumToShift];
+
+ for (C=this._buffer.length - 1; C>=NumToShift; C--) {
+ this._buffer[C] = this._buffer[C - NumToShift];
+ }
+
+ for (C=0; C<NumToShift; C++)
+ {
+ Alpha = C / NumToShift;
+
+ this._buffer[C] = this._buffer[NumToShift] * Alpha + this._vf.destination * (1 - Alpha);
+ }
+ }
+ else
+ {
+ this._previousValue = (NumToShift == this._buffer.length) ? this._buffer[0] : this._vf.destination;
+
+ for (C = 0; C<this._buffer.length; C++) {
+ this._buffer[C] = this._vf.destination;
+ }
+ }
+
+ this._bufferEndTime += NumToShift * this._stepTime;
+ }
+
+ return Frac;
+ }
+ }
+ )
+);
+/** @namespace x3dom.nodeTypes */
+/*
+ * X3DOM JavaScript Library
+ * http://www.x3dom.org
+ *
+ * (C)2009 Fraunhofer IGD, Darmstadt, Germany
+ * Dual licensed under the MIT and GPL
+ */
+
+/* ### ScalarDamper ### */
+x3dom.registerNodeType(
+ "ScalarDamper",
+ "Followers",
+ defineClass(x3dom.nodeTypes.X3DDamperNode,
+
+ /**
+ * Constructor for ScalarDamper
+ * @constructs x3dom.nodeTypes.ScalarDamper
+ * @x3d 3.3
+ * @component Followers
+ * @status experimental
+ * @extends x3dom.nodeTypes.X3DDamperNode
+ * @param {Object} [ctx=null] - context object, containing initial settings like namespace
+ * @classdesc The ScalarDamper animates transitions for single float values. If the value field is routed to a
+ * transparency field of a Material node, then, whenever the destination field receives a single float value,
+ * the ScalarDamper node creates a transition from its current value to the newly set value.
+ */
+ function (ctx) {
+ x3dom.nodeTypes.ScalarDamper.superClass.call(this, ctx);
+
+
+ /**
+ * The field initialDestination should be set to the same value than initialValue unless a transition to a
+ * certain value is to be created right after the scene is loaded or right after the ScalarDamper node is
+ * created dynamically.
+ * @var {x3dom.fields.SFFloat} initialDestination
+ * @memberof x3dom.nodeTypes.ScalarDamper
+ * @initvalue 0
+ * @field x3d
+ * @instance
+ */
+ this.addField_SFFloat(ctx, 'initialDestination', 0);
+
+ /**
+ * The field initialValue can be used to set the initial value of the node.
+ * @var {x3dom.fields.SFFloat} initialValue
+ * @memberof x3dom.nodeTypes.ScalarDamper
+ * @initvalue 0
+ * @field x3d
+ * @instance
+ */
+ this.addField_SFFloat(ctx, 'initialValue', 0);
+
+
+ /**
+ * The current value.
+ * @var {x3dom.fields.SFFloat} value
+ * @memberof x3dom.nodeTypes.ScalarDamper
+ * @initvalue 0
+ * @field x3d
+ * @instance
+ */
+ this.addField_SFFloat(ctx, 'value', 0);
+
+ /**
+ * The target value.
+ * @var {x3dom.fields.SFFloat} destination
+ * @memberof x3dom.nodeTypes.ScalarDamper
+ * @initvalue 0
+ * @field x3d
+ * @instance
+ */
+ this.addField_SFFloat(ctx, 'destination', 0);
+
+ this._value0 = 0;
+ this._value1 = 0;
+ this._value2 = 0;
+ this._value3 = 0;
+ this._value4 = 0;
+ this._value5 = 0;
+
+ this.initialize();
+
+ },
+ {
+ fieldChanged: function(fieldName)
+ {
+ if (fieldName === "tolerance")
+ {
+ this._eps = this._vf.tolerance < 0 ? 0.001 : this._vf.tolerance;
+ }
+ else if (fieldName.indexOf("destination") >= 0)
+ {
+ if (Math.abs(this._value0 - this._vf.destination) > this._eps) {
+ this._value0 = this._vf.destination;
+
+ if (!this._vf.isActive) {
+ //this._lastTick = 0;
+ this.postMessage('isActive', true);
+ }
+ }
+ }
+ else if (fieldName.indexOf("value") >= 0)
+ {
+ this._value1 = this._vf.value;
+ this._value2 = this._vf.value;
+ this._value3 = this._vf.value;
+ this._value4 = this._vf.value;
+ this._value5 = this._vf.value;
+ this._lastTick = 0;
+
+ this.postMessage('value', this._value5);
+
+ if (!this._vf.isActive) {
+ this._lastTick = 0;
+ this.postMessage('isActive', true);
+ }
+ }
+ },
+
+ initialize: function()
+ {
+ this._value0 = this._vf.initialDestination;
+ this._value1 = this._vf.initialValue;
+ this._value2 = this._vf.initialValue;
+ this._value3 = this._vf.initialValue;
+ this._value4 = this._vf.initialValue;
+ this._value5 = this._vf.initialValue;
+ this._lastTick = 0;
+
+ var active = (Math.abs(this._value0 - this._value1) > this._eps);
+ if (this._vf.isActive !== active) {
+ this.postMessage('isActive', active);
+ }
+ },
+
+ tick: function(now)
+ {
+ //if (!this._vf.isActive)
+ // return false;
+
+ if (!this._lastTick)
+ {
+ this._lastTick = now;
+ return false;
+ }
+
+ var delta = now - this._lastTick;
+
+ var alpha = Math.exp(-delta / this._vf.tau);
+
+ this._value1 = this._vf.order > 0 && this._vf.tau ?
+ this._value0 + alpha * (this._value1 - this._value0) : this._value0;
+
+ this._value2 = this._vf.order > 1 && this._vf.tau ?
+ this._value1 + alpha * (this._value2 - this._value1) : this._value1;
+
+ this._value3 = this._vf.order > 2 && this._vf.tau ?
+ this._value2 + alpha * (this._value3 - this._value2) : this._value2;
+
+ this._value4 = this._vf.order > 3 && this._vf.tau ?
+ this._value3 + alpha * (this._value4 - this._value3) : this._value3;
+
+ this._value5 = this._vf.order > 4 && this._vf.tau ?
+ this._value4 + alpha * (this._value5 - this._value4) : this._value4;
+
+ var dist = Math.abs(this._value1 - this._value0);
+
+ if (this._vf.order > 1)
+ {
+ var dist2 = Math.abs(this._value2 - this._value1);
+ if (dist2 > dist) {dist = dist2;}
+ }
+ if (this._vf.order > 2)
+ {
+ var dist3 = Math.abs(this._value3 - this._value2);
+ if (dist3 > dist) {dist = dist3;}
+ }
+ if (this._vf.order > 3)
+ {
+ var dist4 = Math.abs(this._value4 - this._value3);
+ if (dist4 > dist) {dist = dist4;}
+ }
+ if (this._vf.order > 4)
+ {
+ var dist5 = Math.abs(this._value5 - this._value4);
+ if (dist5 > dist) {dist = dist5;}
+ }
+
+ if (dist <= this._eps)
+ {
+ this._value1 = this._value0;
+ this._value2 = this._value0;
+ this._value3 = this._value0;
+ this._value4 = this._value0;
+ this._value5 = this._value0;
+
+ this.postMessage('value', this._value0);
+ this.postMessage('isActive', false);
+
+ this._lastTick = 0;
+
+ return false;
+ }
+
+ this.postMessage('value', this._value5);
+
+ this._lastTick = now;
+
+ return true;
+ }
+ }
+ )
+);
+/** @namespace x3dom.nodeTypes */
+/*
+ * X3DOM JavaScript Library
+ * http://www.x3dom.org
+ *
+ * (C)2009 Fraunhofer IGD, Darmstadt, Germany
+ * Dual licensed under the MIT and GPL
+ */
+
+/* ### CoordinateDamper ### */
+x3dom.registerNodeType(
+ "CoordinateDamper",
+ "Followers",
+ defineClass(x3dom.nodeTypes.X3DDamperNode,
+
+ /**
+ * Constructor for CoordinateDamper
+ * @constructs x3dom.nodeTypes.CoordinateDamper
+ * @x3d 3.3
+ * @component Followers
+ * @status experimental
+ * @extends x3dom.nodeTypes.X3DDamperNode
+ * @param {Object} [ctx=null] - context object, containing initial settings like namespace
+ * @classdesc The CoordinateChaser animates transitions for array of 3D vectors (e.g., the coordinates of a
+ * mesh). Whenever it receives an array of 3D vectors, the value_changed creates a transition from its
+ * current value to the newly set number. It creates a smooth transition that ends duration seconds after the
+ * last number has been received.
+ */
+ function (ctx) {
+ x3dom.nodeTypes.CoordinateDamper.superClass.call(this, ctx);
+
+
+ /**
+ * The field initialDestination should be set to the same value than initialValue unless a transition to a
+ * certain value is to be created right after the scene is loaded or right after the CoordinateChaser node
+ * is created dynamically.
+ * @var {x3dom.fields.MFVec3f} initialDestination
+ * @memberof x3dom.nodeTypes.CoordinateDamper
+ * @initvalue []
+ * @field x3d
+ * @instance
+ */
+ this.addField_MFVec3f(ctx, 'initialDestination', []);
+
+ /**
+ * The field initialValue can be used to set the initial value.
+ * @var {x3dom.fields.MFVec3f} initialValue
+ * @memberof x3dom.nodeTypes.CoordinateDamper
+ * @initvalue []
+ * @field x3d
+ * @instance
+ */
+ this.addField_MFVec3f(ctx, 'initialValue', []);
+
+
+ /**
+ * The current coordinate value
+ * @var {x3dom.fields.MFVec3f} value
+ * @memberof x3dom.nodeTypes.CoordinateDamper
+ * @initvalue []
+ * @field x3d
+ * @instance
+ */
+ this.addField_MFVec3f(ctx, 'value', []);
+
+ /**
+ * The target coordinate value
+ * @var {x3dom.fields.MFVec3f} destination
+ * @memberof x3dom.nodeTypes.CoordinateDamper
+ * @initvalue []
+ * @field x3dom
+ * @instance
+ */
+ this.addField_MFVec3f(ctx, 'destination', []);
+
+ x3dom.debug.logWarning("CoordinateDamper NYI");
+
+ }
+ )
+);
+/** @namespace x3dom.nodeTypes */
+/*
+ * X3DOM JavaScript Library
+ * http://www.x3dom.org
+ *
+ * (C)2009 Fraunhofer IGD, Darmstadt, Germany
+ * Dual licensed under the MIT and GPL
+ */
+
+/* ### TexCoordDamper2D ### */
+x3dom.registerNodeType(
+ "TexCoordDamper2D",
+ "Followers",
+ defineClass(x3dom.nodeTypes.X3DDamperNode,
+
+ /**
+ * Constructor for TexCoordDamper2D
+ * @constructs x3dom.nodeTypes.TexCoordDamper2D
+ * @x3d 3.3
+ * @component Followers
+ * @status experimental
+ * @extends x3dom.nodeTypes.X3DDamperNode
+ * @param {Object} [ctx=null] - context object, containing initial settings like namespace
+ * @classdesc The TexCoordDamper2D node animates transitions for an array of 2D vectors (e.g., the texture
+ * coordinates of a mesh). Whenever the destination field receives an array of 2D vectors, value begins
+ * sending an array of the same length, where each element moves from its current value towards the value at
+ * the same position in the array received.
+ */
+ function (ctx) {
+ x3dom.nodeTypes.TexCoordDamper2D.superClass.call(this, ctx);
+
+
+ /**
+ * The field initialDestination should be set to the same value than initialValue unless a transition to a
+ * certain 2D vector value is to be created right after the scene is loaded or right after the
+ * CoordinateDamper node is created dynamically.
+ * @var {x3dom.fields.MFVec2f} initialDestination
+ * @memberof x3dom.nodeTypes.TexCoordDamper2D
+ * @initvalue []
+ * @field x3d
+ * @instance
+ */
+ this.addField_MFVec2f(ctx, 'initialDestination', []);
+
+ /**
+ * The field initialValue can be used to set the initial value of value_changed.
+ * @var {x3dom.fields.MFVec2f} initialValue
+ * @memberof x3dom.nodeTypes.TexCoordDamper2D
+ * @initvalue []
+ * @field x3d
+ * @instance
+ */
+ this.addField_MFVec2f(ctx, 'initialValue', []);
+
+
+ /**
+ * The current value.
+ * @var {x3dom.fields.MFVec2f} value
+ * @memberof x3dom.nodeTypes.TexCoordDamper2D
+ * @initvalue []
+ * @field x3d
+ * @instance
+ */
+ this.addField_MFVec2f(ctx, 'value', []);
+
+ /**
+ * The target value.
+ * @var {x3dom.fields.MFVec2f} destination
+ * @memberof x3dom.nodeTypes.TexCoordDamper2D
+ * @initvalue []
+ * @field x3d
+ * @instance
+ */
+ this.addField_MFVec2f(ctx, 'destination', []);
+
+ x3dom.debug.logWarning("TexCoordDamper2D NYI");
+
+ }
+ )
+);
+/** @namespace x3dom.nodeTypes */
+/*
+ * X3DOM JavaScript Library
+ * http://www.x3dom.org
+ *
+ * (C)2009 Fraunhofer IGD, Darmstadt, Germany
+ * Dual licensed under the MIT and GPL
+ */
+
+// ### X3DInterpolatorNode ###
+x3dom.registerNodeType(
+ "X3DInterpolatorNode",
+ "Interpolation",
+ defineClass(x3dom.nodeTypes.X3DChildNode,
+
+ /**
+ * Constructor for X3DInterpolatorNode
+ * @constructs x3dom.nodeTypes.X3DInterpolatorNode
+ * @x3d 3.3
+ * @component Interpolation
+ * @status experimental
+ * @extends x3dom.nodeTypes.X3DChildNode
+ * @param {Object} [ctx=null] - context object, containing initial settings like namespace
+ * @classdesc The abstract node X3DInterpolatorNode forms the basis for all types of interpolators.
+ */
+ function (ctx) {
+ x3dom.nodeTypes.X3DInterpolatorNode.superClass.call(this, ctx);
+
+
+ /**
+ * The key field contains the list of key times, the keyValue field contains values for the target field, one complete set of values for each key.
+ * Interpolator nodes containing no keys in the key field shall not produce any events.
+ * However, an input event that replaces an empty key field with one that contains keys will cause the interpolator node to produce events the next time that a set_fraction event is received.
+ * @var {x3dom.fields.MFFloat} key
+ * @memberof x3dom.nodeTypes.X3DInterpolatorNode
+ * @initvalue []
+ * @field x3d
+ * @instance
+ */
+ this.addField_MFFloat(ctx, 'key', []);
+
+ /**
+ * The set_fraction inputOnly field receives an SFFloat event and causes the interpolator node function to evaluate, resulting in a value_changed output event of the specified type with the same timestamp as the set_fraction event.
+ * @var {x3dom.fields.SFFloat} set_fraction
+ * @memberof x3dom.nodeTypes.X3DInterpolatorNode
+ * @initvalue 0
+ * @field x3d
+ * @instance
+ */
+ this.addField_SFFloat(ctx, 'set_fraction', 0);
+
+ },
+ {
+ linearInterp: function (time, interp) {
+ if (time <= this._vf.key[0])
+ return this._vf.keyValue[0];
+
+ else if (time >= this._vf.key[this._vf.key.length-1])
+ return this._vf.keyValue[this._vf.key.length-1];
+
+ for (var i = 0; i < this._vf.key.length-1; ++i) {
+ if ((this._vf.key[i] < time) && (time <= this._vf.key[i+1]))
+ return interp( this._vf.keyValue[i], this._vf.keyValue[i+1],
+ (time - this._vf.key[i]) / (this._vf.key[i+1] - this._vf.key[i]) );
+ }
+ return this._vf.keyValue[0];
+ }
+ }
+ )
+);
+/** @namespace x3dom.nodeTypes */
+/*
+ * X3DOM JavaScript Library
+ * http://www.x3dom.org
+ *
+ * (C)2009 Fraunhofer IGD, Darmstadt, Germany
+ * Dual licensed under the MIT and GPL
+ */
+
+// ### OrientationInterpolator ###
+x3dom.registerNodeType(
+ "OrientationInterpolator",
+ "Interpolation",
+ defineClass(x3dom.nodeTypes.X3DInterpolatorNode,
+
+ /**
+ * Constructor for OrientationInterpolator
+ * @constructs x3dom.nodeTypes.OrientationInterpolator
+ * @x3d 3.3
+ * @component Interpolation
+ * @status full
+ * @extends x3dom.nodeTypes.X3DInterpolatorNode
+ * @param {Object} [ctx=null] - context object, containing initial settings like namespace
+ * @classdesc The OrientationInterpolator node interpolates among a list of rotation values specified in the keyValue field to produce an SFRotation value_changed event.
+ * These rotations are absolute in object space and therefore are not cumulative.
+ * The keyValue field shall contain exactly as many rotations as there are key frames in the key field.
+ * An orientation represents the final position of an object after a rotation has been applied.
+ * An OrientationInterpolator interpolates between two orientations by computing the shortest path on the unit sphere between the two orientations.
+ * The interpolation is linear in arc length along this path. The results are undefined if the two orientations are diagonally opposite.
+ */
+ function (ctx) {
+ x3dom.nodeTypes.OrientationInterpolator.superClass.call(this, ctx);
+
+
+ /**
+ * Defines the set of data points, that are used for interpolation.
+ * If two consecutive keyValue values exist such that the arc length between them is greater than π, the interpolation will take place on the arc complement.
+ * For example, the interpolation between the orientations (0, 1, 0, 0) and (0, 1, 0, 5.0) is equivalent to the rotation between the orientations (0, 1, 0, 2Ï€) and (0, 1, 0, 5.0).
+ * @var {x3dom.fields.MFRotation} keyValue
+ * @memberof x3dom.nodeTypes.OrientationInterpolator
+ * @initvalue []
+ * @field x3d
+ * @instance
+ */
+ this.addField_MFRotation(ctx, 'keyValue', []);
+
+ },
+ {
+ fieldChanged: function(fieldName)
+ {
+ if(fieldName === "set_fraction")
+ {
+ var value = this.linearInterp(this._vf.set_fraction, function (a, b, t) {
+ return a.slerp(b, t);
+ });
+ this.postMessage('value_changed', value);
+ }
+ }
+ }
+ )
+);
+/** @namespace x3dom.nodeTypes */
+/*
+ * X3DOM JavaScript Library
+ * http://www.x3dom.org
+ *
+ * (C)2009 Fraunhofer IGD, Darmstadt, Germany
+ * Dual licensed under the MIT and GPL
+ */
+
+// ### PositionInterpolator ###
+x3dom.registerNodeType(
+ "PositionInterpolator",
+ "Interpolation",
+ defineClass(x3dom.nodeTypes.X3DInterpolatorNode,
+
+ /**
+ * Constructor for PositionInterpolator
+ * @constructs x3dom.nodeTypes.PositionInterpolator
+ * @x3d 3.3
+ * @component Interpolation
+ * @status full
+ * @extends x3dom.nodeTypes.X3DInterpolatorNode
+ * @param {Object} [ctx=null] - context object, containing initial settings like namespace
+ * @classdesc The PositionInterpolator node linearly interpolates among a list of 3D vectors to produce an SFVec3f value_changed event. The keyValue field shall contain exactly as many values as in the key field.
+ */
+ function (ctx) {
+ x3dom.nodeTypes.PositionInterpolator.superClass.call(this, ctx);
+
+
+ /**
+ * Defines the set of data points, that are used for interpolation.
+ * @var {x3dom.fields.MFVec3f} keyValue
+ * @memberof x3dom.nodeTypes.PositionInterpolator
+ * @initvalue []
+ * @field x3d
+ * @instance
+ */
+ this.addField_MFVec3f(ctx, 'keyValue', []);
+
+ },
+ {
+ fieldChanged: function(fieldName)
+ {
+ if(fieldName === "set_fraction")
+ {
+ var value = this.linearInterp(this._vf.set_fraction, function (a, b, t) {
+ return a.multiply(1.0-t).add(b.multiply(t));
+ });
+
+ this.postMessage('value_changed', value);
+ }
+ }
+ }
+ )
+);
+/** @namespace x3dom.nodeTypes */
+/*
+ * X3DOM JavaScript Library
+ * http://www.x3dom.org
+ *
+ * (C)2009 Fraunhofer IGD, Darmstadt, Germany
+ * Dual licensed under the MIT and GPL
+ */
+
+// ### NormalInterpolator ###
+x3dom.registerNodeType(
+ "NormalInterpolator",
+ "Interpolation",
+ defineClass(x3dom.nodeTypes.X3DInterpolatorNode,
+
+ /**
+ * Constructor for NormalInterpolator
+ * @constructs x3dom.nodeTypes.NormalInterpolator
+ * @x3d 3.3
+ * @component Interpolation
+ * @status full
+ * @extends x3dom.nodeTypes.X3DInterpolatorNode
+ * @param {Object} [ctx=null] - context object, containing initial settings like namespace
+ * @classdesc The NormalInterpolator node interpolates among a list of normal vector sets specified by the keyValue field to produce an MFVec3f value_changed event.
+ * The output vector, value_changed, shall be a set of normalized vectors.
+ * Values in the keyValue field shall be of unit length.
+ * The number of normals in the keyValue field shall be an integer multiple of the number of key frames in the key field.
+ * That integer multiple defines how many normals will be contained in the value_changed events.
+ */
+ function (ctx) {
+ x3dom.nodeTypes.NormalInterpolator.superClass.call(this, ctx);
+
+
+ /**
+ * Defines the set of data points, that are used for interpolation.
+ * Values in the keyValue field shall be of unit length.
+ * @var {x3dom.fields.MFVec3f} keyValue
+ * @memberof x3dom.nodeTypes.NormalInterpolator
+ * @initvalue []
+ * @field x3d
+ * @instance
+ */
+ this.addField_MFVec3f(ctx, 'keyValue', []);
+
+ },
+ {
+ fieldChanged: function(fieldName)
+ {
+ if(fieldName === "set_fraction")
+ {
+ var value = this.linearInterp(this._vf.set_fraction, function (a, b, t) {
+ return a.multiply(1.0-t).add(b.multiply(t)).normalize();
+ });
+
+ this.postMessage('value_changed', value);
+ }
+ }
+ }
+ )
+);
+/** @namespace x3dom.nodeTypes */
+/*
+ * X3DOM JavaScript Library
+ * http://www.x3dom.org
+ *
+ * (C)2009 Fraunhofer IGD, Darmstadt, Germany
+ * Dual licensed under the MIT and GPL
+ */
+
+// ### ColorInterpolator ###
+x3dom.registerNodeType(
+ "ColorInterpolator",
+ "Interpolation",
+ defineClass(x3dom.nodeTypes.X3DInterpolatorNode,
+
+ /**
+ * Constructor for ColorInterpolator
+ * @constructs x3dom.nodeTypes.ColorInterpolator
+ * @x3d 3.3
+ * @component Interpolation
+ * @status full
+ * @extends x3dom.nodeTypes.X3DInterpolatorNode
+ * @param {Object} [ctx=null] - context object, containing initial settings like namespace
+ * @classdesc The ColorInterpolator node interpolates among a list of MFColor key values to produce an SFColor (RGB) value_changed event.
+ * The number of colours in the keyValue field shall be equal to the number of key frames in the key field.
+ * A linear interpolation using the value of set_fraction as input is performed in HSV space.
+ * The results are undefined when interpolating between two consecutive keys with complementary hues.
+ */
+ function (ctx) {
+ x3dom.nodeTypes.ColorInterpolator.superClass.call(this, ctx);
+
+
+ /**
+ * Defines the set of data points, that are used for interpolation.
+ * @var {x3dom.fields.MFColor} keyValue
+ * @memberof x3dom.nodeTypes.ColorInterpolator
+ * @initvalue []
+ * @field x3d
+ * @instance
+ */
+ this.addField_MFColor(ctx, 'keyValue', []);
+
+ },
+ {
+ fieldChanged: function(fieldName)
+ {
+ if(fieldName === "set_fraction")
+ {
+ // FIXME; perform color interpolation in HSV space
+ var value = this.linearInterp(this._vf.set_fraction, function (a, b, t) {
+ return a.multiply(1.0-t).add(b.multiply(t));
+ });
+
+ this.postMessage('value_changed', value);
+ }
+ }
+ }
+ )
+);
+/** @namespace x3dom.nodeTypes */
+/*
+ * X3DOM JavaScript Library
+ * http://www.x3dom.org
+ *
+ * (C)2009 Fraunhofer IGD, Darmstadt, Germany
+ * Dual licensed under the MIT and GPL
+ */
+
+// ### ScalarInterpolator ###
+x3dom.registerNodeType(
+ "ScalarInterpolator",
+ "Interpolation",
+ defineClass(x3dom.nodeTypes.X3DInterpolatorNode,
+
+ /**
+ * Constructor for ScalarInterpolator
+ * @constructs x3dom.nodeTypes.ScalarInterpolator
+ * @x3d 3.3
+ * @component Interpolation
+ * @status full
+ * @extends x3dom.nodeTypes.X3DInterpolatorNode
+ * @param {Object} [ctx=null] - context object, containing initial settings like namespace
+ * @classdesc The ScalarInterpolator node linearly interpolates among a list of SFFloat values to produce an SFFloat value_changed event.
+ * This interpolator is appropriate for any parameter defined using a single floating point value.
+ */
+ function (ctx) {
+ x3dom.nodeTypes.ScalarInterpolator.superClass.call(this, ctx);
+
+
+ /**
+ * Defines the set of data points, that are used for interpolation.
+ * @var {x3dom.fields.MFFloat} keyValue
+ * @memberof x3dom.nodeTypes.ScalarInterpolator
+ * @initvalue []
+ * @field x3d
+ * @instance
+ */
+ this.addField_MFFloat(ctx, 'keyValue', []);
+
+ },
+ {
+ fieldChanged: function(fieldName)
+ {
+ if(fieldName === "set_fraction")
+ {
+ var value = this.linearInterp(this._vf.set_fraction, function (a, b, t) {
+ return (1.0-t)*a + t*b;
+ });
+
+ this.postMessage('value_changed', value);
+ }
+ }
+ }
+ )
+);
+/** @namespace x3dom.nodeTypes */
+/*
+ * X3DOM JavaScript Library
+ * http://www.x3dom.org
+ *
+ * (C)2009 Fraunhofer IGD, Darmstadt, Germany
+ * Dual licensed under the MIT and GPL
+ */
+
+// ### CoordinateInterpolator ###
+x3dom.registerNodeType(
+ "CoordinateInterpolator",
+ "Interpolation",
+ defineClass(x3dom.nodeTypes.X3DInterpolatorNode,
+
+ /**
+ * Constructor for CoordinateInterpolator
+ * @constructs x3dom.nodeTypes.CoordinateInterpolator
+ * @x3d 3.3
+ * @component Interpolation
+ * @status full
+ * @extends x3dom.nodeTypes.X3DInterpolatorNode
+ * @param {Object} [ctx=null] - context object, containing initial settings like namespace
+ * @classdesc The CoordinateInterpolator node linearly interpolates among a list of MFVec3f values to produce an MFVec3f value_changed event.
+ * The number of coordinates in the keyValue field shall be an integer multiple of the number of key frames in the key field.
+ * That integer multiple defines how many coordinates will be contained in the value_changed events.
+ */
+ function (ctx) {
+ x3dom.nodeTypes.CoordinateInterpolator.superClass.call(this, ctx);
+
+
+ /**
+ * Defines the set of data points, that are used for interpolation.
+ * @var {x3dom.fields.MFVec3f} keyValue
+ * @memberof x3dom.nodeTypes.CoordinateInterpolator
+ * @initvalue []
+ * @field x3d
+ * @instance
+ */
+ this.addField_MFVec3f(ctx, 'keyValue', []);
+
+ if (ctx && ctx.xmlNode.hasAttribute('keyValue')) {
+ this._vf.keyValue = []; // FIXME!!!
+
+ var arr = x3dom.fields.MFVec3f.parse(ctx.xmlNode.getAttribute('keyValue'));
+ var key = this._vf.key.length > 0 ? this._vf.key.length : 1;
+ var len = arr.length / key;
+ for (var i=0; i<key; i++) {
+ var val = new x3dom.fields.MFVec3f();
+ for (var j=0; j<len; j++) {
+ val.push( arr[i*len+j] );
+ }
+ this._vf.keyValue.push(val);
+ }
+ }
+
+ },
+ {
+ fieldChanged: function(fieldName)
+ {
+ if(fieldName === "set_fraction")
+ {
+ var value = this.linearInterp(this._vf.set_fraction, function (a, b, t) {
+ var val = new x3dom.fields.MFVec3f();
+ for (var i=0; i<a.length; i++)
+ val.push(a[i].multiply(1.0-t).add(b[i].multiply(t)));
+
+ return val;
+ });
+
+ this.postMessage('value_changed', value);
+ }
+ }
+ }
+ )
+);
+
+/** @namespace x3dom.nodeTypes */
+/*
+ * Based on code originally provided by
+ * http://www.x3dom.org
+ *
+ * (C)2014 Toshiba Corporation, Japan.
+ * Dual licensed under the MIT and GPL
+ */
+
+// ### SplinePositionInterpolator ###
+x3dom.registerNodeType(
+ "SplinePositionInterpolator",
+ "Interpolation",
+ defineClass(x3dom.nodeTypes.X3DInterpolatorNode,
+
+ /**
+ * Constructor for SplinePositionInterpolator
+ * @constructs x3dom.nodeTypes.SplinePositionInterpolator
+ * @x3d 3.3
+ * @component Interpolation
+ * @status experimental
+ * @extends x3dom.nodeTypes.X3DInterpolatorNode
+ * @param {Object} [ctx=null] - context object, containing initial settings like namespace
+ * @classdesc The SplinePositionInterpolator node non-linearly interpolates among a list of 3D vectors to produce an SFVec3f value_changed event. The keyValue, keyVelocity, and key fields shall each have the same number of values.
+ */
+ function (ctx) {
+ x3dom.nodeTypes.SplinePositionInterpolator.superClass.call(this, ctx);
+
+ /**
+ * Defines the set of data points, that are used for interpolation.
+ * @var {x3dom.fields.MFVec3f} keyValue
+ * @memberof x3dom.nodeTypes.SplinePositionInterpolator
+ * @initvalue []
+ * @field x3d
+ * @instance
+ */
+ this.addField_MFVec3f(ctx, 'keyValue', []);
+
+ /**
+ * Defines the set of velocity vectors, that are used for interpolation.
+ * @var {x3dom.fields.MFVec3f} keyVelocity
+ * @memberof x3dom.nodeTypes.SplinePositionInterpolator
+ * @initvalue []
+ * @field x3d
+ * @instance
+ */
+ this.addField_MFVec3f(ctx, 'keyVelocity', []);
+
+ /**
+ * Specifies whether the interpolator should provide a closed loop, with continuous velocity vectors as the interpolator transitions from the last key to the first key.
+ * @var {x3dom.fields.SFBool} closed
+ * @memberof x3dom.nodeTypes.SplinePositionInterpolator
+ * @initvalue false
+ * @field x3d
+ * @instance
+ */
+ this.addField_SFBool(ctx, 'closed', false);
+
+ /**
+ * Specifies whether the velocity vectors are to be transformed into normalized tangency vectors.
+ * @var {x3dom.fields.SFBool} normalizeVelocity
+ * @memberof x3dom.nodeTypes.SplinePositionInterpolator
+ * @initvalue false
+ * @field x3d
+ * @instance
+ */
+ this.addField_SFBool(ctx, 'normalizeVelocity', false);
+
+ /******** Private variables and functions ***********/
+
+ /* dtot is the sum of the distance between all adjacent keys.
+ * dtot = SUM{i=0, i < n-1}(|vi - vi+1|)
+ */
+ this.dtot = 0.0;
+
+ /* Non-uniform interval adjusted velocity vectors
+ */
+ this.T0 = [];
+ this.T1 = [];
+
+ /* Checks sanity. Node is sane if (|key| == |key_value|) and (|key| == |key_velocity| or |key_velocity| == 0 or (|key_velocity| == 2 and |key| >= 2))
+ */
+ this.checkSanity = function() {
+ var sane = (this._vf.key.length == this._vf.keyValue.length) &&
+ ((this._vf.key.length == this._vf.keyVelocity.length) || (this._vf.keyVelocity.length == 2 && this._vf.key.length >= 2) || (this._vf.keyVelocity.length == 0));
+ if(!sane)
+ x3dom.debug.logWarning("SplinePositionInterpolator Node: 'key' , 'keyValue' and/or 'keyVelocity' fields have inappropriate sizes");
+ };
+
+ /* Calculate dtot (sum of distances between all adjacent keys)
+ */
+ this.calcDtot = function()
+ {
+ this.dtot = 0.0;
+ for(var i = 0; i < this._vf.key.length-1; i++)
+ {
+ this.dtot += Math.abs(this._vf.key[i] - this._vf.key[i+1]);
+ }
+ };
+
+ /* Calculate non-uniform interval adjusted velocity vectors
+ */
+ this.calcAdjustedKeyVelocity = function()
+ {
+ var i, Ti, F_plus_i, F_minus_i;
+ var N = this._vf.key.length;
+
+ // If velocities are defined at all the control points, ignore 'closed' field
+ if(this._vf.keyVelocity.length == N)
+ {
+ for(i = 0; i < N; i++)
+ {
+ Ti = this._vf.keyVelocity[i];
+
+ if(this._vf.normalizeVelocity)
+ Ti = Ti.multiply(this.dtot / Ti.length());
+
+ F_plus_i = (i == 0 || i == N-1) ? 1.0 : 2.0 * (this._vf.key[i] - this._vf.key[i-1]) / (this._vf.key[i+1] - this._vf.key[i-1]);
+ F_minus_i= (i == 0 || i == N-1) ? 1.0 : 2.0 * (this._vf.key[i+1] - this._vf.key[i]) / (this._vf.key[i+1] - this._vf.key[i-1]);
+
+ this.T0[i] = Ti.multiply(F_plus_i);
+ this.T1[i] = Ti.multiply(F_minus_i);
+ }
+ }
+ // if only first and last velocities are specified, ignore 'closed' field
+ else if(this._vf.keyVelocity.length == 2 && N > 2)
+ {
+ for(i = 0; i < N; i++)
+ {
+ if(i == 0)
+ Ti = this._vf.keyVelocity[0];
+ else if(i == N-1)
+ Ti = this._vf.keyVelocity[1];
+ else
+ Ti = this._vf.keyValue[i+1].subtract(this._vf.keyValue[i-1]).multiply(0.5);
+
+ if(this._vf.normalizeVelocity)
+ Ti = Ti.multiply(this.dtot / Ti.length());
+
+ F_plus_i = (i == 0 || i == N-1) ? 1.0 : 2.0 * (this._vf.key[i] - this._vf.key[i-1]) / (this._vf.key[i+1] - this._vf.key[i-1]);
+ F_minus_i= (i == 0 || i == N-1) ? 1.0 : 2.0 * (this._vf.key[i+1] - this._vf.key[i]) / (this._vf.key[i+1] - this._vf.key[i-1]);
+
+ this.T0[i] = Ti.multiply(F_plus_i);
+ this.T1[i] = Ti.multiply(F_minus_i);
+ }
+ }
+ // velocities are unspecified
+ else
+ {
+ // ignore closed if first and last keyValues are not equal
+ var closed = this._vf.closed && this._vf.keyValue[0].equals(this._vf.keyValue[N-1], 0.00001);
+
+ for(i = 0; i < N; i++)
+ {
+ if((i == 0 || i == N-1) && !closed)
+ {
+ this.T0[i] = new x3dom.fields.SFVec3f(0, 0, 0);
+ this.T1[i] = new x3dom.fields.SFVec3f(0, 0, 0);
+ continue;
+ }
+ else if((i == 0 || i == N-1) && closed)
+ {
+ Ti = this._vf.keyValue[1].subtract(this._vf.keyValue[N-2]).multiply(0.5);
+ if(i == 0) {
+ F_plus_i = 2.0 * (this._vf.key[0] - this._vf.key[N-2]) / (this._vf.key[1] - this._vf.key[N-2]);
+ F_minus_i= 2.0 * (this._vf.key[1] - this._vf.key[0]) / (this._vf.key[1] - this._vf.key[N-2]);
+ }
+ else {
+ F_plus_i = 2.0 * (this._vf.key[N-1] - this._vf.key[N-2]) / (this._vf.key[1] - this._vf.key[N-2]);
+ F_minus_i= 2.0 * (this._vf.key[1] - this._vf.key[N-1]) / (this._vf.key[1] - this._vf.key[N-2]);
+ }
+ F_plus_i = 2.0 * (this._vf.key[N-1] - this._vf.key[N-2]) / (this._vf.key[N-2] - this._vf.key[1]);
+ F_minus_i= 2.0 * (this._vf.key[1] - this._vf.key[0]) / (this._vf.key[N-2] - this._vf.key[1]);
+ }
+ else
+ {
+ Ti = this._vf.keyValue[i+1].subtract(this._vf.keyValue[i-1]).multiply(0.5);
+ F_plus_i = 2.0 * (this._vf.key[i] - this._vf.key[i-1]) / (this._vf.key[i+1] - this._vf.key[i-1]);
+ F_minus_i= 2.0 * (this._vf.key[i+1] - this._vf.key[i]) / (this._vf.key[i+1] - this._vf.key[i-1]);
+ }
+
+ this.T0[i] = Ti.multiply(F_plus_i);
+ this.T1[i] = Ti.multiply(F_minus_i);
+ }
+ }
+ };
+
+ this.checkSanity();
+ this.calcDtot();
+ this.calcAdjustedKeyVelocity();
+ },
+ {
+ fieldChanged: function(fieldName)
+ {
+ switch(fieldName)
+ {
+ case 'key':
+ case 'keyValue':
+ case 'keyVelocity':
+ {
+ this.checkSanity();
+ this.calcDtot();
+ this.calcAdjustedKeyVelocity();
+ break;
+ }
+ case 'closed':
+ case 'normalizeVelocity':
+ {
+ this.calcAdjustedKeyVelocity();
+ break;
+ }
+ case 'set_fraction':
+ {
+ var value;
+
+ if(this._vf.key.length > 0.0) {
+ if (this._vf.set_fraction <= this._vf.key[0])
+ value = x3dom.fields.SFVec3f.copy(this._vf.keyValue[0]);
+
+ else if (this._vf.set_fraction >= this._vf.key[this._vf.key.length-1])
+ value = x3dom.fields.SFVec3f.copy(this._vf.keyValue[this._vf.key.length-1]);
+ }
+
+ for(var i = 0; i < this._vf.key.length-1; i++) {
+ if ((this._vf.key[i] < this._vf.set_fraction) && (this._vf.set_fraction <= this._vf.key[i+1])) {
+ var s = (this._vf.set_fraction - this._vf.key[i]) / (this._vf.key[i+1]-this._vf.key[i]);
+
+ var S_H = new x3dom.fields.SFVec4f(2.0*s*s*s - 3.0*s*s + 1.0, -2.0*s*s*s + 3.0*s*s, s*s*s - 2.0*s*s + s, s*s*s - s*s);
+ value = new x3dom.fields.SFVec3f(S_H.x * this._vf.keyValue[i].x + S_H.y * this._vf.keyValue[i+1].x + S_H.z * this.T0[i].x + S_H.w * this.T1[i+1].x,
+ S_H.x * this._vf.keyValue[i].y + S_H.y * this._vf.keyValue[i+1].y + S_H.z * this.T0[i].y + S_H.w * this.T1[i+1].y,
+ S_H.x * this._vf.keyValue[i].z + S_H.y * this._vf.keyValue[i+1].z + S_H.z * this.T0[i].z + S_H.w * this.T1[i+1].z);
+ break;
+ }
+ }
+
+ if(value !== undefined)
+ this.postMessage('value_changed', value);
+ else
+ x3dom.debug.logWarning("SplinePositionInterpolator Node: value_changed is undefined!");
+ }
+ }
+ }
+ }
+ )
+);
+/** @namespace x3dom.nodeTypes */
+/*
+ * X3DOM JavaScript Library
+ * http://www.x3dom.org
+ *
+ * (C)2009 Fraunhofer IGD, Darmstadt, Germany
+ * Dual licensed under the MIT and GPL
+ */
+
+// ### TimeSensor ###
+x3dom.registerNodeType(
+ "TimeSensor",
+ "Time",
+ defineClass(x3dom.nodeTypes.X3DSensorNode,
+
+ /**
+ * Constructor for TimeSensor
+ * @constructs x3dom.nodeTypes.TimeSensor
+ * @x3d 3.3
+ * @component Time
+ * @status full
+ * @extends x3dom.nodeTypes.X3DSensorNode
+ * @param {Object} [ctx=null] - context object, containing initial settings like namespace
+ * @classdesc TimeSensor nodes generate events as time passes.
+ */
+ function (ctx) {
+ x3dom.nodeTypes.TimeSensor.superClass.call(this, ctx);
+
+ if (ctx)
+ ctx.doc._nodeBag.timer.push(this);
+ else
+ x3dom.debug.logWarning("TimeSensor: No runtime context found!");
+
+
+ /**
+ * The "cycle" of a TimeSensor node lasts for cycleInterval seconds. The value of cycleInterval shall be greater than zero.
+ * @var {x3dom.fields.SFTime} cycleInterval
+ * @range [0, inf]
+ * @memberof x3dom.nodeTypes.TimeSensor
+ * @initvalue 1
+ * @field x3d
+ * @instance
+ */
+ this.addField_SFTime(ctx, 'cycleInterval', 1);
+
+
+ /**
+ * Specifies whether the timer cycle loops.
+ * @var {x3dom.fields.SFBool} loop
+ * @memberof x3dom.nodeTypes.TimeSensor
+ * @initvalue false
+ * @field x3d
+ * @instance
+ */
+ this.addField_SFBool(ctx, 'loop', false);
+
+ /**
+ * Sets the startTime for the cycle.
+ * @var {x3dom.fields.SFTime} startTime
+ * @memberof x3dom.nodeTypes.TimeSensor
+ * @initvalue 0
+ * @field x3d
+ * @instance
+ */
+ this.addField_SFTime(ctx, 'startTime', 0);
+
+ /**
+ * Sets a time for the timer to stop.
+ * @var {x3dom.fields.SFTime} stopTime
+ * @memberof x3dom.nodeTypes.TimeSensor
+ * @initvalue 0
+ * @field x3d
+ * @instance
+ */
+ this.addField_SFTime(ctx, 'stopTime', 0);
+
+ /**
+ * Sets a time for the timer to pause.
+ * @var {x3dom.fields.SFTime} pauseTime
+ * @memberof x3dom.nodeTypes.TimeSensor
+ * @initvalue 0
+ * @field x3d
+ * @instance
+ */
+ this.addField_SFTime(ctx, 'pauseTime', 0);
+
+ /**
+ * Sets a time for the timer to resume from pause.
+ * @var {x3dom.fields.SFTime} resumeTime
+ * @memberof x3dom.nodeTypes.TimeSensor
+ * @initvalue 0
+ * @field x3d
+ * @instance
+ */
+ this.addField_SFTime(ctx, 'resumeTime', 0);
+
+
+ /**
+ * A cycleTime outputOnly field can be used for synchronization purposes such as sound with animation.
+ * The value of a cycleTime event will be equal to the time at the beginning of the current cycle. A cycleTime event is generated at the beginning of every cycle, including the cycle starting at startTime.
+ * The first cycleTime event for a TimeSensor node can be used as an alarm (single pulse at a specified time).
+ * @var {x3dom.fields.SFTime} cycleTime
+ * @memberof x3dom.nodeTypes.TimeSensor
+ * @initvalue 0
+ * @field x3d
+ * @instance
+ */
+ this.addField_SFTime(ctx, 'cycleTime', 0);
+
+ /**
+ * The elapsedTime outputOnly field delivers the current elapsed time since the TimeSensor was activated and running, cumulative in seconds and not counting any time while in a paused state.
+ * @var {x3dom.fields.SFTime} elapsedTime
+ * @memberof x3dom.nodeTypes.TimeSensor
+ * @initvalue 0
+ * @field x3d
+ * @instance
+ */
+ this.addField_SFTime(ctx, 'elapsedTime', 0);
+
+ /**
+ * fraction_changed events output a floating point value in the closed interval [0, 1]. At startTime the value of fraction_changed is 0. After startTime, the value of fraction_changed in any cycle will progress through the range (0.0, 1.0].
+ * @var {x3dom.fields.SFFloat} fraction_changed
+ * @memberof x3dom.nodeTypes.TimeSensor
+ * @initvalue 0
+ * @field x3d
+ * @instance
+ */
+ this.addField_SFFloat(ctx, 'fraction_changed', 0);
+
+ /**
+ * Outputs whether the timer is active.
+ * @var {x3dom.fields.SFBool} isActive
+ * @memberof x3dom.nodeTypes.TimeSensor
+ * @initvalue false
+ * @field x3d
+ * @instance
+ */
+ this.addField_SFBool(ctx, 'isActive', false);
+
+ /**
+ * Outputs whether the timer is paused.
+ * @var {x3dom.fields.SFBool} isPaused
+ * @memberof x3dom.nodeTypes.TimeSensor
+ * @initvalue false
+ * @field x3d
+ * @instance
+ */
+ this.addField_SFBool(ctx, 'isPaused', false);
+
+ /**
+ * The time event sends the absolute time for a given tick of the TimeSensor node.
+ * @var {x3dom.fields.SFTime} time
+ * @memberof x3dom.nodeTypes.TimeSensor
+ * @initvalue 0
+ * @field x3d
+ * @instance
+ */
+ this.addField_SFTime(ctx, 'time', 0);
+
+
+ /**
+ *
+ * @var {x3dom.fields.SFBool} first
+ * @memberof x3dom.nodeTypes.TimeSensor
+ * @initvalue true
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFBool(ctx,'first', true);
+
+ /**
+ *
+ * @var {x3dom.fields.SFFloat} firstCycle
+ * @memberof x3dom.nodeTypes.TimeSensor
+ * @initvalue 0.0
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFFloat(ctx,'firstCycle', 0.0);
+
+ this._prevCycle = -1;
+ this._lastTime = 0;
+ this._cycleStopTime = 0;
+ this._activatedTime = 0;
+
+ if (this._vf.startTime > 0) {
+ this._updateCycleStopTime();
+ }
+
+ this._backupStartTime = this._vf.startTime;
+ this._backupStopTime = this._vf.stopTime;
+ this._backupCycleInterval = this._vf.cycleInterval;
+
+ },
+ {
+ tick: function (time)
+ {
+ if (!this._vf.enabled) {
+ this._lastTime = time;
+ return false;
+ }
+
+ var isActive = ( this._vf.cycleInterval > 0 &&
+ time >= this._vf.startTime &&
+ (time < this._vf.stopTime || this._vf.stopTime <= this._vf.startTime) &&
+ (this._vf.loop == true || (this._vf.loop == false && time < this._cycleStopTime)) );
+
+ if (isActive && !this._vf.isActive) {
+ this.postMessage('isActive', true);
+ this._activatedTime = time;
+ }
+
+ // Checking for this._vf.isActive allows the dispatch of 'final events' (before deactivation)
+ if (isActive || this._vf.isActive) {
+ this.postMessage('elapsedTime', time - this._activatedTime);
+
+ var isPaused = ( time >= this._vf.pauseTime && this._vf.pauseTime > this._vf.resumeTime );
+
+ if (isPaused && !this._vf.isPaused) {
+ this.postMessage('isPaused', true);
+ this.postMessage('pauseTime', time);
+ } else if (!isPaused && this._vf.isPaused) {
+ this.postMessage('isPaused', false);
+ this.postMessage('resumeTime', time);
+ }
+
+ if (!isPaused) {
+ var cycleFrac = this._getCycleAt(time);
+ var cycle = Math.floor(cycleFrac);
+
+ var cycleTime = this._vf.startTime + cycle*this._vf.cycleInterval;
+ var adjustTime = 0;
+
+ if (this._vf.stopTime > this._vf.startTime &&
+ this._lastTime < this._vf.stopTime && time >= this._vf.stopTime)
+ adjustTime = this._vf.stopTime;
+ else if (this._lastTime < cycleTime && time >= cycleTime)
+ adjustTime = cycleTime;
+
+ if( adjustTime > 0 ) {
+ time = adjustTime;
+ cycleFrac = this._getCycleAt(time);
+ cycle = Math.floor(cycleFrac);
+ }
+
+ var fraction = cycleFrac - cycle;
+
+ if (fraction < x3dom.fields.Eps) {
+ fraction = ( this._lastTime < this._vf.startTime ? 0.0 : 1.0 );
+ this.postMessage('cycleTime', time);
+ }
+
+ this.postMessage('fraction_changed', fraction);
+
+ this.postMessage('time', time);
+ }
+ }
+
+ if (!isActive && this._vf.isActive)
+ this.postMessage('isActive', false);
+
+ this._lastTime = time;
+
+ return true;
+ },
+
+ fieldChanged: function(fieldName)
+ {
+ if (fieldName == "enabled") {
+ // TODO; eval other relevant outputs
+ if (!this._vf.enabled && this._vf.isActive) {
+ this.postMessage('isActive', false);
+ }
+ }
+ else if (fieldName == "startTime") {
+ // Spec: Should be ignored when active. (Restore old value)
+ if (this._vf.isActive) {
+ this._vf.startTime = this._backupStartTime;
+ return;
+ }
+
+ this._backupStartTime = this._vf.startTime;
+ this._updateCycleStopTime();
+ }
+ else if (fieldName == "stopTime") {
+ // Spec: Should be ignored when active and less than startTime. (Restore old value)
+ if (this._vf.isActive && this._vf.stopTime <= this._vf.startTime) {
+ this._vf.stopTime = this._backupStopTime;
+ return;
+ }
+
+ this._backupStopTime = this._vf.stopTime;
+ }
+ else if (fieldName == "cycleInterval") {
+ // Spec: Should be ignored when active. (Restore old value)
+ if (this._vf.isActive) {
+ this._vf.cycleInterval = this._backupCycleInterval;
+ return;
+ }
+
+ this._backupCycleInterval = this._vf.cycleInterval;
+ }
+ else if (fieldName == "loop") {
+ this._updateCycleStopTime();
+ }
+ },
+
+ parentRemoved: function(parent)
+ {
+ if (this._parentNodes.length === 0) {
+ var doc = this.findX3DDoc();
+
+ for (var i=0, n=doc._nodeBag.timer.length; i<n; i++) {
+ if (doc._nodeBag.timer[i] === this) {
+ doc._nodeBag.timer.splice(i, 1);
+ }
+ }
+ }
+ },
+
+ _getCycleAt: function(time)
+ {
+ return Math.max( 0.0, time - this._vf.startTime ) / this._vf.cycleInterval;
+ },
+
+ _updateCycleStopTime: function()
+ {
+ if (this._vf.loop == false) {
+ var now = new Date().getTime() / 1000;
+ var cycleToStop = Math.floor(this._getCycleAt(now)) + 1;
+
+ this._cycleStopTime = this._vf.startTime + cycleToStop*this._vf.cycleInterval;
+ }
+ else {
+ this._cycleStopTime = 0;
+ }
+ }
+ }
+ )
+);
+/** @namespace x3dom.nodeTypes */
+/*
+ * X3DOM JavaScript Library
+ * http://www.x3dom.org
+ *
+ * (C)2009 Fraunhofer IGD, Darmstadt, Germany
+ * Dual licensed under the MIT and GPL
+ */
+
+/* ### X3DTimeDependentNode ### */
+x3dom.registerNodeType(
+ "X3DTimeDependentNode",
+ "Time",
+ defineClass(x3dom.nodeTypes.X3DChildNode,
+
+ /**
+ * Constructor for X3DTimeDependentNode
+ * @constructs x3dom.nodeTypes.X3DTimeDependentNode
+ * @x3d 3.3
+ * @component Time
+ * @status experimental
+ * @extends x3dom.nodeTypes.X3DChildNode
+ * @param {Object} [ctx=null] - context object, containing initial settings like namespace
+ * @classdesc This abstract node type is the base node type from which all time-dependent nodes are derived.
+ */
+ function (ctx) {
+ x3dom.nodeTypes.X3DTimeDependentNode.superClass.call(this, ctx);
+
+
+ /**
+ * Specifies whether the timer cycle loops.
+ * @var {x3dom.fields.SFBool} loop
+ * @memberof x3dom.nodeTypes.X3DTimeDependentNode
+ * @initvalue false
+ * @field x3d
+ * @instance
+ */
+ this.addField_SFBool(ctx, 'loop', false);
+
+ }
+ )
+);
+/** @namespace x3dom.nodeTypes */
+/*
+ * X3DOM JavaScript Library
+ * http://www.x3dom.org
+ *
+ * (C)2009 Fraunhofer IGD, Darmstadt, Germany
+ * Dual licensed under the MIT and GPL
+ */
+
+// ### Anchor ###
+x3dom.registerNodeType(
+ "Anchor",
+ "Networking",
+ defineClass(x3dom.nodeTypes.X3DGroupingNode,
+
+ /**
+ * Constructor for Anchor
+ * @constructs x3dom.nodeTypes.Anchor
+ * @x3d 3.3
+ * @component Networking
+ * @status full
+ * @extends x3dom.nodeTypes.X3DGroupingNode
+ * @param {Object} [ctx=null] - context object, containing initial settings like namespace
+ * @classdesc Anchor is a Grouping node that can contain most nodes. Clicking Anchored geometry loads content specified by the url field.
+ * Loaded content completely replaces current content, if parameter is same window.
+ * Hint: insert a Shape node before adding geometry or Appearance.
+ */
+ function (ctx) {
+ x3dom.nodeTypes.Anchor.superClass.call(this, ctx);
+
+
+ /**
+ * Address of replacement world, activated by clicking Anchor geometry. Hint: jump to a world's internal viewpoint by appending viewpoint name (e.g. #ViewpointName, someOtherCoolWorld.wrl#GrandTour). Hint: jump to a local viewpoint by only using viewpoint name (e.g. #GrandTour). Hint: Strings can have multiple values, so separate each string by quote marks [ 'http://www.url1.org' 'http://www.url2.org' 'etc.' ]. Hint: XML encoding for ' is ampersandquot; (a character entity). Warning: strictly match directory and filename capitalization for http links! Hint: can replace embedded blank(s) in url queries with %20 for each blank character.
+ * @var {x3dom.fields.MFString} url
+ * @memberof x3dom.nodeTypes.Anchor
+ * @initvalue []
+ * @field x3d
+ * @instance
+ */
+ this.addField_MFString(ctx, 'url', []);
+
+ /**
+ * Passed parameter that signals web browser how to redirect url loading. Each string shall consist of "keyword=value" pairs. Hint: set parameter to target=_blank or target=_extern to load target url with a system-specific application. target=_self or target=_intern will open url in current x3d-browser window. Hint: set parameter to target=frame_name to load target url into another frame. Hint: Strings can have multiple values, so separate each string by quote marks. [ 'http://www.url1.org' 'http://www.url2.org' 'etc.' ]. Interchange profile hint: this field may be ignored.
+ * @var {x3dom.fields.MFString} parameter
+ * @memberof x3dom.nodeTypes.Anchor
+ * @initvalue []
+ * @field x3d
+ * @instance
+ */
+ this.addField_MFString(ctx, 'parameter', []);
+
+ /**
+ * The description field in the Anchor node specifies a textual description of the Anchor node.
+ * This may be used by browser-specific user interfaces that wish to present users with more detailed information about the Anchor.
+ * @var {x3dom.fields.SFString} description
+ * @memberof x3dom.nodeTypes.Anchor
+ * @initvalue []
+ * @field x3d
+ * @instance
+ */
+ this.addField_SFString(ctx, 'description', "");
+
+ },
+ {
+ doIntersect: function(line) {
+ var isect = false;
+ for (var i=0; i<this._childNodes.length; i++) {
+ if (this._childNodes[i]) {
+ isect = this._childNodes[i].doIntersect(line) || isect;
+ }
+ }
+ return isect;
+ },
+
+ handleTouch: function() {
+ var url = this._vf.url.length ? this._vf.url[0] : "";
+ var aPos = url.search("#");
+ var anchor = "";
+ if (aPos >= 0)
+ anchor = url.slice(aPos+1);
+
+ var param = this._vf.parameter.length ? this._vf.parameter[0] : "";
+ var tPos = param.search("target=");
+ var target = "";
+ if (tPos >= 0)
+ target = param.slice(tPos+7);
+
+ // TODO: implement #Viewpoint bind
+ // http://www.web3d.org/files/specifications/19775-1/V3.2/Part01/components/networking.html#Anchor
+ x3dom.debug.logInfo("Anchor url=" + url + ", target=" + target + ", #viewpoint=" + anchor);
+
+ if(target.length !=0 || target != "_self") {
+ window.open(this._nameSpace.getURL(url), target);
+ }
+ else {
+ window.location = this._nameSpace.getURL(url);
+ }
+ }
+ }
+ )
+);
+
+/** @namespace x3dom.nodeTypes */
+/*
+ * X3DOM JavaScript Library
+ * http://www.x3dom.org
+ *
+ * (C)2009 Fraunhofer IGD, Darmstadt, Germany
+ * Dual licensed under the MIT and GPL
+ */
+
+// ### Inline ###
+x3dom.registerNodeType(
+ "Inline",
+ "Networking",
+ defineClass(x3dom.nodeTypes.X3DGroupingNode,
+
+ /**
+ * Constructor for Inline
+ * @constructs x3dom.nodeTypes.Inline
+ * @x3d 3.3
+ * @component Networking
+ * @status full
+ * @extends x3dom.nodeTypes.X3DGroupingNode
+ * @param {Object} [ctx=null] - context object, containing initial settings like namespace
+ * @classdesc Inline is a Grouping node that can load nodes from another X3D scene via url.
+ */
+ function (ctx) {
+ x3dom.nodeTypes.Inline.superClass.call(this, ctx);
+
+
+ /**
+ * Each specified URL shall refer to a valid X3D file that contains a list of children nodes, prototypes and routes at the top level. Hint: Strings can have multiple values, so separate each string by quote marks. Warning: strictly match directory and filename capitalization for http links!
+ * @var {x3dom.fields.MFString} url
+ * @memberof x3dom.nodeTypes.Inline
+ * @initvalue []
+ * @field x3d
+ * @instance
+ */
+ this.addField_MFString(ctx, 'url', []);
+
+ /**
+ * Specifies whether the X3D file specified by the url field is loaded. Hint: use LoadSensor to detect when loading is complete. TRUE: load immediately (it's also possible to load the URL at a later time by sending a TRUE event to the load field); FALSE: no action is taken (by sending a FALSE event to the load field of a previously loaded Inline, the contents of the Inline will be unloaded from the scene graph)
+ * @var {x3dom.fields.SFBool} load
+ * @memberof x3dom.nodeTypes.Inline
+ * @initvalue true
+ * @field x3d
+ * @instance
+ */
+ this.addField_SFBool(ctx, 'load', true);
+
+ /**
+ * Specifies the namespace of the Inline node.
+ * @var {x3dom.fields.MFString} nameSpaceName
+ * @memberof x3dom.nodeTypes.Inline
+ * @initvalue []
+ * @field x3dom
+ * @instance
+ */
+ this.addField_MFString(ctx, 'nameSpaceName', []);
+
+ /**
+ * Specifies whether the DEF value is used as id when no other id is set.
+ * @var {x3dom.fields.SFBool} mapDEFToID
+ * @memberof x3dom.nodeTypes.Inline
+ * @initvalue false
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFBool(ctx, 'mapDEFToID', false);
+
+ this.initDone = false;
+ this.count = 0;
+ this.numRetries = x3dom.nodeTypes.Inline.MaximumRetries;
+
+ },
+ {
+ fieldChanged: function (fieldName)
+ {
+ if (fieldName == "url") {
+ if (this._vf.nameSpaceName.length != 0) {
+ var node = this._xmlNode;
+ if (node && node.hasChildNodes())
+ {
+ while ( node.childNodes.length >= 1 )
+ {
+ node.removeChild( node.firstChild );
+ }
+ }
+ }
+ this.loadInline();
+ }
+ else if (fieldName == "render") {
+ this.invalidateVolume();
+ //this.invalidateCache();
+ }
+ },
+
+ nodeChanged: function ()
+ {
+ if (!this.initDone) {
+ this.initDone = true;
+ this.loadInline();
+ }
+ },
+
+ fireEvents: function(eventType)
+ {
+ if ( this._xmlNode &&
+ (this._xmlNode['on'+eventType] ||
+ this._xmlNode.hasAttribute('on'+eventType) ||
+ this._listeners[eventType]) )
+ {
+ var event = {
+ target: this._xmlNode,
+ type: eventType,
+ error: (eventType == "error") ? "XMLHttpRequest Error" : "",
+ cancelBubble: false,
+ stopPropagation: function() { this.cancelBubble = true; }
+ };
+
+ try {
+ var attrib = this._xmlNode["on" + eventType];
+
+ if (typeof(attrib) === "function") {
+ attrib.call(this._xmlNode, event);
+ }
+ else {
+ var funcStr = this._xmlNode.getAttribute("on" + eventType);
+ var func = new Function('event', funcStr);
+ func.call(this._xmlNode, event);
+ }
+
+ var list = this._listeners[eventType];
+ if (list) {
+ for (var i = 0; i < list.length; i++) {
+ list[i].call(this._xmlNode, event);
+ }
+ }
+ }
+ catch(ex) {
+ x3dom.debug.logException(ex);
+ }
+ }
+ },
+
+ loadInline: function ()
+ {
+ var that = this;
+
+ var xhr = new window.XMLHttpRequest();
+ if (xhr.overrideMimeType)
+ xhr.overrideMimeType('text/xml'); //application/xhtml+xml
+
+ xhr.onreadystatechange = function ()
+ {
+ if (xhr.readyState != 4) {
+ // still loading
+ //x3dom.debug.logInfo('Loading inlined data... (readyState: ' + xhr.readyState + ')');
+ return xhr;
+ }
+
+ if (xhr.status === x3dom.nodeTypes.Inline.AwaitTranscoding) {
+ if (that.count < that.numRetries)
+ {
+ that.count++;
+ var refreshTime = +xhr.getResponseHeader("Refresh") || 5;
+ x3dom.debug.logInfo('XHR status: ' + xhr.status + ' - Await Transcoding (' + that.count + '/' + that.numRetries + '): ' +
+ 'Next request in ' + refreshTime + ' seconds');
+
+ window.setTimeout(function() {
+ that._nameSpace.doc.downloadCount -= 1;
+ that.loadInline();
+ }, refreshTime * 1000);
+ return xhr;
+ }
+ else
+ {
+ x3dom.debug.logError('XHR status: ' + xhr.status + ' - Await Transcoding (' + that.count + '/' + that.numRetries + '): ' +
+ 'No Retries left');
+ that._nameSpace.doc.downloadCount -= 1;
+ that.count = 0;
+ return xhr;
+ }
+ }
+ else if ((xhr.status !== 200) && (xhr.status !== 0)) {
+ that.fireEvents("error");
+ x3dom.debug.logError('XHR status: ' + xhr.status + ' - XMLHttpRequest requires web server running!');
+
+ that._nameSpace.doc.downloadCount -= 1;
+ that.count = 0;
+ return xhr;
+ }
+ else if ((xhr.status == 200) || (xhr.status == 0)) {
+ that.count = 0;
+ }
+
+ x3dom.debug.logInfo('Inline: downloading '+that._vf.url[0]+' done.');
+
+ var inlScene = null, newScene = null, nameSpace = null, xml = null;
+
+ if (navigator.appName != "Microsoft Internet Explorer")
+ xml = xhr.responseXML;
+ else
+ xml = new DOMParser().parseFromString(xhr.responseText, "text/xml");
+
+ //TODO; check if exists and FIXME: it's not necessarily the first scene in the doc!
+ if (xml !== undefined && xml !== null)
+ {
+ inlScene = xml.getElementsByTagName('Scene')[0] ||
+ xml.getElementsByTagName('scene')[0];
+ }
+ else {
+ that.fireEvents("error");
+ }
+
+ if (inlScene)
+ {
+ var nsName = (that._vf.nameSpaceName.length != 0) ?
+ that._vf.nameSpaceName.toString().replace(' ','') : "";
+ nameSpace = new x3dom.NodeNameSpace(nsName, that._nameSpace.doc);
+
+ var url = that._vf.url.length ? that._vf.url[0] : "";
+ if ((url[0] === '/') || (url.indexOf(":") >= 0))
+ nameSpace.setBaseURL(url);
+ else
+ nameSpace.setBaseURL(that._nameSpace.baseURL + url);
+
+ newScene = nameSpace.setupTree(inlScene);
+ that._nameSpace.addSpace(nameSpace);
+
+ if(that._vf.nameSpaceName.length != 0)
+ {
+ Array.forEach ( inlScene.childNodes, function (childDomNode)
+ {
+ if(childDomNode instanceof Element)
+ {
+ setNamespace(that._vf.nameSpaceName, childDomNode, that._vf.mapDEFToID);
+ that._xmlNode.appendChild(childDomNode);
+ }
+ } );
+ }
+ }
+ else {
+ if (xml && xml.localName)
+ x3dom.debug.logError('No Scene in ' + xml.localName);
+ else
+ x3dom.debug.logError('No Scene in resource');
+ }
+
+ // trick to free memory, assigning a property to global object, then deleting it
+ var global = x3dom.getGlobal();
+
+ if (that._childNodes.length > 0 && that._childNodes[0] && that._childNodes[0]._nameSpace)
+ that._nameSpace.removeSpace(that._childNodes[0]._nameSpace);
+
+ while (that._childNodes.length !== 0)
+ global['_remover'] = that.removeChild(that._childNodes[0]);
+
+ delete global['_remover'];
+
+ if (newScene)
+ {
+ that.addChild(newScene);
+
+ that.invalidateVolume();
+ //that.invalidateCache();
+
+ that._nameSpace.doc.downloadCount -= 1;
+ that._nameSpace.doc.needRender = true;
+ x3dom.debug.logInfo('Inline: added ' + that._vf.url[0] + ' to scene.');
+
+ // recalc changed scene bounding box twice
+ var theScene = that._nameSpace.doc._scene;
+
+ if (theScene) {
+ theScene.invalidateVolume();
+ //theScene.invalidateCache();
+
+ window.setTimeout( function() {
+ that.invalidateVolume();
+ //that.invalidateCache();
+
+ theScene.updateVolume();
+ that._nameSpace.doc.needRender = true;
+ }, 1000 );
+ }
+
+ that.fireEvents("load");
+ }
+
+ newScene = null;
+ nameSpace = null;
+ inlScene = null;
+ xml = null;
+
+ return xhr;
+ };
+
+ if (this._vf.url.length && this._vf.url[0].length)
+ {
+ var xhrURI = this._nameSpace.getURL(this._vf.url[0]);
+
+ xhr.open('GET', xhrURI, true);
+
+ this._nameSpace.doc.downloadCount += 1;
+
+ try {
+ xhr.send(null);
+ }
+ catch(ex) {
+ this.fireEvents("error");
+ x3dom.debug.logError(this._vf.url[0] + ": " + ex);
+ }
+ }
+ }
+ }
+ )
+);
+
+x3dom.nodeTypes.Inline.AwaitTranscoding = 202; // Parameterizable retry state for Transcoder
+x3dom.nodeTypes.Inline.MaximumRetries = 15; // Parameterizable maximum number of retries
+
+function setNamespace(prefix, childDomNode, mapDEFToID)
+{
+ if(childDomNode instanceof Element && childDomNode.__setAttribute !== undefined) {
+
+ if(childDomNode.hasAttribute('id') ) {
+ childDomNode.__setAttribute('id', prefix.toString().replace(' ','') +'__'+ childDomNode.getAttribute('id'));
+ } else if (childDomNode.hasAttribute('DEF') && mapDEFToID){
+ childDomNode.__setAttribute('id', prefix.toString().replace(' ','') +'__'+ childDomNode.getAttribute('DEF'));
+ // workaround for Safari
+ if (!childDomNode.id)
+ childDomNode.id = childDomNode.__getAttribute('id');
+ }
+ }
+
+ if(childDomNode.hasChildNodes()){
+ Array.forEach ( childDomNode.childNodes, function (children) {
+ setNamespace(prefix, children, mapDEFToID);
+ } );
+ }
+}
+
+/** @namespace x3dom.nodeTypes */
+/*
+ * X3DOM JavaScript Library
+ * http://www.x3dom.org
+ *
+ * (C)2009 Fraunhofer IGD, Darmstadt, Germany
+ * Dual licensed under the MIT and GPL
+ */
+
+// ### MultiPart ###
+x3dom.registerNodeType(
+ "MultiPart",
+ "Networking",
+ defineClass(x3dom.nodeTypes.Inline,
+
+ /**
+ * Constructor for MultiPart
+ * @constructs x3dom.nodeTypes.MultiPart
+ * @x3d x.x
+ * @component Networking
+ * @extends x3dom.nodeTypes.Inline
+ * @param {Object} [ctx=null] - context object, containing initial settings like namespace
+ * @classdesc Multipart node
+ */
+ function (ctx) {
+ x3dom.nodeTypes.MultiPart.superClass.call(this, ctx);
+
+ /**
+ * Specifies the url to the IDMap.
+ * @var {x3dom.fields.MFString} urlIDMap
+ * @memberof x3dom.nodeTypes.MultiPart
+ * @initvalue []
+ * @field x3dom
+ * @instance
+ */
+ this.addField_MFString(ctx, 'urlIDMap', []);
+
+ /**
+ * Defines whether the shape is pickable.
+ * @var {x3dom.fields.SFBool} isPickable
+ * @memberof x3dom.nodeTypes.MultiPart
+ * @initvalue true
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFBool(ctx, 'isPickable', true);
+
+ /**
+ * Defines the shape type for sorting.
+ * @var {x3dom.fields.SFString} sortType
+ * @range [auto, transparent, opaque]
+ * @memberof x3dom.nodeTypes.MultiPart
+ * @initvalue 'auto'
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFString(ctx, 'sortType', 'auto');
+
+ /**
+ * Specifies whether backface-culling is used. If solid is TRUE only front-faces are drawn.
+ * @var {x3dom.fields.SFBool} solid
+ * @memberof x3dom.nodeTypes.MultiPart
+ * @initvalue true
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFBool(ctx, 'solid', false);
+
+ /**
+ * Change render order manually.
+ * @var {x3dom.fields.SFInt32} sortKey
+ * @memberof x3dom.nodeTypes.MultiPart
+ * @initvalue 0
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFInt32(ctx, 'sortKey', 0);
+
+ /**
+ * Set the initial visibility.
+ * @var {x3dom.fields.SFInt32} initialVisibility
+ * @range [auto, visible, invisible]
+ * @memberof x3dom.nodeTypes.MultiPart
+ * @initvalue 'auto'
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFString(ctx, 'initialVisibility', 'auto');
+
+ this._idMap = null;
+ this._inlineNamespace = null;
+ this._highlightedParts = [];
+ this._minId = 0;
+ this._maxId = 0;
+ this._lastId = -1;
+ this._lastClickedId = -1;
+ this._lastButton = 0;
+ this._identifierToPartId = [];
+ this._identifierToAppId = [];
+ this._visiblePartsPerShape = [];
+ this._partVolume = [];
+ this._partVisibility = [];
+ this._originalColor = [];
+ this._materials = [];
+
+ },
+ {
+ fieldChanged: function (fieldName)
+ {
+ if (fieldName == "url") {
+ if (this._vf.nameSpaceName.length != 0) {
+ var node = this._xmlNode;
+ if (node && node.hasChildNodes())
+ {
+ while ( node.childNodes.length >= 1 )
+ {
+ node.removeChild( node.firstChild );
+ }
+ }
+ }
+ this.loadInline();
+ }
+ else if (fieldName == "render") {
+ this.invalidateVolume();
+ //this.invalidateCache();
+ }
+ },
+
+ nodeChanged: function ()
+ {
+ if (!this.initDone) {
+ this.initDone = true;
+ this.loadIDMap();
+ }
+ },
+
+ getVolume: function ()
+ {
+ var vol = this._graph.volume;
+
+ if (!this.volumeValid() && this._vf.render)
+ {
+ for (var i=0; i<this._partVisibility.length; i++)
+ {
+ if (!this._partVisibility[i])
+ continue;
+
+ var childVol = this._partVolume[i];
+
+ if (childVol && childVol.isValid())
+ vol.extendBounds(childVol.min, childVol.max);
+ }
+ }
+
+ return vol;
+ },
+
+ handleEvents: function(e)
+ {
+ if( this._inlineNamespace ) {
+ var colorMap = this._inlineNamespace.defMap["MultiMaterial_ColorMap"];
+ var emissiveMap = this._inlineNamespace.defMap["MultiMaterial_EmissiveMap"];
+ var specularMap = this._inlineNamespace.defMap["MultiMaterial_SpecularMap"];
+ var visibilityMap = this._inlineNamespace.defMap["MultiMaterial_VisibilityMap"];
+
+ //Check for Background press and release
+ if (e.pickedId == -1 && e.button != 0) {
+ this._lastClickedId = -1;
+ this._lastButton = e.button;
+ } else if (e.pickedId == -1 && e.button == 0) {
+ this._lastClickedId = -1;
+ this._lastButton = 0;
+ }
+
+ if (e.pickedId != -1) {
+ e.part = new x3dom.Parts(this, [e.pickedId - this._minId], colorMap, emissiveMap, specularMap, visibilityMap);
+ e.partID = this._idMap.mapping[e.pickedId - this._minId].name;
+
+ //fire mousemove event
+ e.type = "mousemove";
+ this.callEvtHandler("onmousemove", e);
+
+ //fire mouseover event
+ e.type = "mouseover";
+ this.callEvtHandler("onmouseover", e);
+
+ //if some mouse button is down fire mousedown event
+ if (!e.mouseup && e.button && e.button != this._lastButton) {
+ e.type = "mousedown";
+ this._lastButton = e.button;
+ if ( this._lastClickedId == -1 ) {
+ this._lastClickedId = e.pickedId;
+ }
+ this.callEvtHandler("onmousedown", e);
+ }
+
+ //if some mouse button is up fire mouseup event
+ if (e.mouseup || (this._lastButton != 0 && e.button == 0)) {
+ e.type = "mouseup";
+ this.callEvtHandler("onmouseup", e);
+ this._lastButton = 0;
+
+ if ( e.pickedId == this._lastClickedId ) {
+ this._lastClickedId = -1;
+ e.type = "click";
+ this.callEvtHandler("onclick", e);
+ }
+
+ this._lastClickedId = -1;
+ }
+
+ //If the picked id has changed we enter+leave a part
+ if (e.pickedId != this._lastId) {
+ if (this._lastId != -1) {
+ e.part = new x3dom.Parts(this, [this._lastId - this._minId], colorMap, emissiveMap, specularMap, visibilityMap);
+ e.partID = this._idMap.mapping[this._lastId - this._minId].name;
+ e.type = "mouseleave";
+ this.callEvtHandler("onmouseleave", e);
+ }
+
+ e.part = new x3dom.Parts(this, [e.pickedId - this._minId], colorMap, emissiveMap, specularMap, visibilityMap);
+ e.partID = this._idMap.mapping[e.pickedId - this._minId].name;
+ e.type = "mouseenter";
+ this.callEvtHandler("onmouseenter", e);
+ this._lastId = e.pickedId;
+ }
+
+ this._lastId = e.pickedId;
+ }
+ else if (this._lastId != -1) {
+ e.part = new x3dom.Parts(this, [this._lastId - this._minId], colorMap, emissiveMap, specularMap, visibilityMap);
+ e.partID = this._idMap.mapping[this._lastId - this._minId].name;
+ e.type = "mouseout";
+ this.callEvtHandler("onmouseout", e);
+ e.type = "mouseleave";
+ this.callEvtHandler("onmouseleave", e);
+ this._lastId = -1;
+ }
+ }
+
+ },
+
+ loadIDMap: function ()
+ {
+ if (this._vf.urlIDMap.length && this._vf.urlIDMap[0].length)
+ {
+ var i;
+
+ var that = this;
+
+ var idMapURI = this._nameSpace.getURL(this._vf.urlIDMap[0]);
+
+ var xhr = new XMLHttpRequest();
+
+ xhr.open("GET", idMapURI, true);
+
+ xhr.onload = function()
+ {
+ that._idMap = JSON.parse(this.responseText);
+
+ //Check if the MultiPart map already initialized
+ if (that._nameSpace.doc._scene._multiPartMap == null) {
+ that._nameSpace.doc._scene._multiPartMap = {numberOfIds: 0, multiParts: []};
+ }
+
+ //Set the ID range this MultiPart is holding
+ that._minId = that._nameSpace.doc._scene._multiPartMap.numberOfIds;
+ that._maxId = that._minId + that._idMap.numberOfIDs - 1;
+
+ //Update the MultiPart map
+ that._nameSpace.doc._scene._multiPartMap.numberOfIds += that._idMap.numberOfIDs;
+ that._nameSpace.doc._scene._multiPartMap.multiParts.push(that);
+
+ //prepare internal shape map
+ for (i=0; i<that._idMap.mapping.length; i++)
+ {
+ if (!that._identifierToPartId[that._idMap.mapping[i].name]) {
+ that._identifierToPartId[that._idMap.mapping[i].name] = [];
+ }
+
+ if (!that._identifierToPartId[that._idMap.mapping[i].appearance]) {
+ that._identifierToPartId[that._idMap.mapping[i].appearance] = [];
+ }
+
+ that._identifierToPartId[that._idMap.mapping[i].name].push(i);
+ that._identifierToPartId[that._idMap.mapping[i].appearance].push(i);
+
+ if (!that._partVolume[i]) {
+ var min = x3dom.fields.SFVec3f.parse(that._idMap.mapping[i].min);
+ var max = x3dom.fields.SFVec3f.parse(that._idMap.mapping[i].max);
+
+ that._partVolume[i] = new x3dom.fields.BoxVolume(min, max);
+ }
+
+ }
+
+ //prepare internal appearance map
+ for (i=0; i<that._idMap.appearance.length; i++)
+ {
+ that._identifierToAppId[that._idMap.appearance[i].name] = i;
+ }
+
+ that.loadInline();
+ };
+
+ xhr.send(null);
+ }
+ },
+
+ createMaterialData: function ()
+ {
+ var diffuseColor, transparency, specularColor, shininess, emissiveColor, ambientIntensity;
+ var backDiffuseColor, backTransparency, backSpecularColor, backShininess, backEmissiveColor, backAmbientIntensity;
+ var rgba_DT = "", rgba_SS = "", rgba_EA = "";
+ var rgba_DT_B = "", rgba_SS_B = "", rgba_EA_B = "";
+
+ var size = Math.ceil(Math.sqrt(this._idMap.numberOfIDs));
+
+ //scale image data array size to the next highest power of two
+ size = x3dom.Utils.nextHighestPowerOfTwo(size);
+ var sizeTwo = size * 2.0;
+
+ var diffuseTransparencyData = size + " " + sizeTwo + " 4";
+ var specularShininessData = size + " " + sizeTwo + " 4";
+ var emissiveAmbientIntensityData = size + " " + sizeTwo + " 4";
+
+ for (var i=0; i<size*size; i++)
+ {
+ if (i < this._idMap.mapping.length)
+ {
+ var appName = this._idMap.mapping[i].appearance;
+ var appID = this._identifierToAppId[appName];
+
+ //AmbientIntensity
+ if (this._idMap.appearance[appID].material.ambientIntensity) {
+ ambientIntensity = this._idMap.appearance[appID].material.ambientIntensity
+ } else {
+ ambientIntensity = "0.2";
+ }
+
+ //BackAmbientIntensity
+ if (this._idMap.appearance[appID].material.backAmbientIntensity) {
+ backAmbientIntensity = this._idMap.appearance[appID].material.backAmbientIntensity
+ } else {
+ backAmbientIntensity = ambientIntensity;
+ }
+
+ //DiffuseColor
+ if (this._idMap.appearance[appID].material.diffuseColor) {
+ diffuseColor = this._idMap.appearance[appID].material.diffuseColor
+ } else {
+ diffuseColor = "0.8 0.8 0.8";
+ }
+
+ //BackDiffuseColor
+ if (this._idMap.appearance[appID].material.backDiffuseColor) {
+ backDiffuseColor = this._idMap.appearance[appID].material.backDiffuseColor
+ } else {
+ backDiffuseColor = diffuseColor;
+ }
+
+ //EmissiveColor
+ if (this._idMap.appearance[appID].material.emissiveColor) {
+ emissiveColor = this._idMap.appearance[appID].material.emissiveColor
+ } else {
+ emissiveColor = "0.0 0.0 0.0";
+ }
+
+ //BackEmissiveColor
+ if (this._idMap.appearance[appID].material.backEmissiveColor) {
+ backEmissiveColor = this._idMap.appearance[appID].material.backEmissiveColor
+ } else {
+ backEmissiveColor = emissiveColor;
+ }
+
+ //Shininess
+ if (this._idMap.appearance[appID].material.shininess) {
+ shininess = this._idMap.appearance[appID].material.shininess;
+ } else {
+ shininess = "0.2";
+ }
+
+ //BackShininess
+ if (this._idMap.appearance[appID].material.backShininess) {
+ backShininess = this._idMap.appearance[appID].material.backShininess;
+ } else {
+ backShininess = shininess;
+ }
+
+ //SpecularColor
+ if (this._idMap.appearance[appID].material.specularColor) {
+ specularColor = this._idMap.appearance[appID].material.specularColor;
+ } else {
+ specularColor = "0 0 0";
+ }
+
+ //BackSpecularColor
+ if (this._idMap.appearance[appID].material.backSpecularColor) {
+ backSpecularColor = this._idMap.appearance[appID].material.backSpecularColor;
+ } else {
+ backSpecularColor = specularColor;
+ }
+
+ //Transparency
+ if (this._idMap.appearance[appID].material.transparency) {
+ transparency = this._idMap.appearance[appID].material.transparency;
+ } else {
+ transparency = "0.0";
+ }
+
+ //BackTransparency
+ if (this._idMap.appearance[appID].material.backTransparency) {
+ backTransparency = this._idMap.appearance[appID].material.backTransparency;
+ } else {
+ backTransparency = transparency;
+ }
+
+ rgba_DT += " " + x3dom.fields.SFColorRGBA.parse(diffuseColor + " " + transparency).toUint();
+ rgba_SS += " " + x3dom.fields.SFColorRGBA.parse(specularColor + " " + shininess).toUint();
+ rgba_EA += " " + x3dom.fields.SFColorRGBA.parse(emissiveColor + " " + ambientIntensity).toUint();
+
+ rgba_DT_B += " " + x3dom.fields.SFColorRGBA.parse(backDiffuseColor + " " + backTransparency).toUint();
+ rgba_SS_B += " " + x3dom.fields.SFColorRGBA.parse(backSpecularColor + " " + backShininess).toUint();
+ rgba_EA_B += " " + x3dom.fields.SFColorRGBA.parse(backEmissiveColor + " " + backAmbientIntensity).toUint();
+
+ this._originalColor[i] = rgba_DT;
+
+ this._materials[i] = new x3dom.MultiMaterial({
+ "ambientIntensity": ambientIntensity,
+ "diffuseColor": x3dom.fields.SFColor.parse(diffuseColor),
+ "emissiveColor": x3dom.fields.SFColor.parse(emissiveColor),
+ "shininess": shininess,
+ "specularColor": x3dom.fields.SFColor.parse(specularColor),
+ "transparency": 1.0 - transparency,
+ "backAmbientIntensity": backAmbientIntensity,
+ "backDiffuseColor": x3dom.fields.SFColor.parse(backDiffuseColor),
+ "backEmissiveColor": x3dom.fields.SFColor.parse(backEmissiveColor),
+ "backShininess": backShininess,
+ "backSpecularColor": x3dom.fields.SFColor.parse(backSpecularColor),
+ "backTransparency": 1.0 - backTransparency
+ });
+ }
+ else
+ {
+ rgba_DT += " 255";
+ rgba_SS += " 255";
+ rgba_EA += " 255";
+
+ rgba_DT_B += " 255";
+ rgba_SS_B += " 255";
+ rgba_EA_B += " 255";
+ }
+ }
+
+ //Combine Front and Back Data
+ diffuseTransparencyData += rgba_DT + rgba_DT_B;
+ specularShininessData += rgba_SS + rgba_SS_B;
+ emissiveAmbientIntensityData += rgba_EA + rgba_EA_B;
+
+ return {
+ "diffuseTransparency": diffuseTransparencyData,
+ "specularShininess": specularShininessData,
+ "emissiveAmbientIntensity": emissiveAmbientIntensityData
+ };
+ },
+
+ createVisibilityData: function ()
+ {
+ var i, j;
+ var size = Math.ceil(Math.sqrt(this._idMap.numberOfIDs));
+
+ //scale image data array size to the next highest power of two
+ size = x3dom.Utils.nextHighestPowerOfTwo(size);
+
+ var visibilityData = size + " " + size + " 1";
+
+ for (i=0; i<size*size; i++)
+ {
+ if (i < this._idMap.mapping.length)
+ {
+ if (this._vf.initialVisibility == 'auto')
+ {
+ //TODO get the Data from JSON
+ visibilityData += " 255";
+
+ if (!this._partVisibility[i]) {
+ this._partVisibility[i] = true;
+ }
+
+ for (j=0; j<this._idMap.mapping[i].usage.length; j++)
+ {
+ if (!this._visiblePartsPerShape[this._idMap.mapping[i].usage[j]]) {
+ this._visiblePartsPerShape[this._idMap.mapping[i].usage[j]] = {val:0, max:0};
+ }
+ this._visiblePartsPerShape[this._idMap.mapping[i].usage[j]].val++;
+ this._visiblePartsPerShape[this._idMap.mapping[i].usage[j]].max++;
+ }
+ }
+ else if (this._vf.initialVisibility == 'visible')
+ {
+ visibilityData += " 255";
+
+ if (!this._partVisibility[i]) {
+ this._partVisibility[i] = true;
+ }
+
+ for (j=0; j<this._idMap.mapping[i].usage.length; j++)
+ {
+ if (!this._visiblePartsPerShape[this._idMap.mapping[i].usage[j]]) {
+ this._visiblePartsPerShape[this._idMap.mapping[i].usage[j]] = {val:0, max:0};
+ }
+ this._visiblePartsPerShape[this._idMap.mapping[i].usage[j]].val++;
+ this._visiblePartsPerShape[this._idMap.mapping[i].usage[j]].max++;
+ }
+ }
+ else if (this._vf.initialVisibility == 'invisible')
+ {
+ visibilityData += " 0";
+
+ if (!this._partVisibility[i]) {
+ this._partVisibility[i] = false;
+ }
+
+ for (j=0; j<this._idMap.mapping[i].usage.length; j++)
+ {
+ if (!this._visiblePartsPerShape[this._idMap.mapping[i].usage[j]]) {
+ this._visiblePartsPerShape[this._idMap.mapping[i].usage[j]] = {val:0, max:0};
+ }
+ this._visiblePartsPerShape[this._idMap.mapping[i].usage[j]].max++;
+ }
+ }
+
+ }
+ else
+ {
+ visibilityData += " 0";
+ }
+ }
+ return visibilityData;
+ },
+
+ replaceMaterials: function (inlScene)
+ {
+ var css, shapeDEF, materialData, visibilityData, appearance;
+ var firstMat = true;
+ if (inlScene && inlScene.hasChildNodes())
+ {
+ materialData = this.createMaterialData();
+ visibilityData = this.createVisibilityData();
+
+ var shapes = inlScene.getElementsByTagName("Shape");
+
+ for (var s=0; s<shapes.length; s++)
+ {
+ shapeDEF = shapes[s].getAttribute("DEF") ||
+ shapes[s].getAttribute("def");
+
+ if(shapeDEF && this._visiblePartsPerShape[shapeDEF] &&
+ this._visiblePartsPerShape[shapeDEF].val == 0)
+ {
+ shapes[s].setAttribute("render", "false");
+ }
+
+ shapes[s].setAttribute("idOffset", this._minId);
+ shapes[s].setAttribute("isPickable", this._vf.isPickable);
+
+ var geometries = shapes[s].getElementsByTagName("BinaryGeometry");
+
+ if (geometries && geometries.length) {
+ for (var g = 0; g < geometries.length; g++) {
+ geometries[g].setAttribute("solid", this._vf.solid);
+ }
+ }
+
+ var appearances = shapes[s].getElementsByTagName("Appearance");
+
+ if (appearances.length)
+ {
+ for (var a = 0; a < appearances.length; a++)
+ {
+ //Remove DEF/USE
+ appearances[a].removeAttribute("DEF");
+ appearances[a].removeAttribute("USE");
+
+ appearances[a].setAttribute("sortType", this._vf.sortType);
+ appearances[a].setAttribute("sortKey", this._vf.sortKey);
+
+ var materials = appearances[a].getElementsByTagName("Material");
+
+ if (materials.length)
+ {
+ //Replace Material
+ if (firstMat) {
+ firstMat = false;
+ css = document.createElement("CommonSurfaceShader");
+ css.setAttribute("DEF", "MultiMaterial");
+
+ var ptDA = document.createElement("PixelTexture");
+ ptDA.setAttribute("containerField", "multiDiffuseAlphaTexture");
+ ptDA.setAttribute("id", "MultiMaterial_ColorMap");
+ ptDA.setAttribute("image", materialData.diffuseTransparency);
+
+ var ptEA = document.createElement("PixelTexture");
+ ptEA.setAttribute("containerField", "multiEmissiveAmbientTexture");
+ ptEA.setAttribute("id", "MultiMaterial_EmissiveMap");
+ ptEA.setAttribute("image", materialData.emissiveAmbientIntensity);
+
+ var ptSS = document.createElement("PixelTexture");
+ ptSS.setAttribute("containerField", "multiSpecularShininessTexture");
+ ptSS.setAttribute("id", "MultiMaterial_SpecularMap");
+ ptSS.setAttribute("image", materialData.specularShininess);
+
+ var ptV = document.createElement("PixelTexture");
+ ptV.setAttribute("containerField", "multiVisibilityTexture");
+ ptV.setAttribute("id", "MultiMaterial_VisibilityMap");
+ ptV.setAttribute("image", visibilityData);
+
+ css.appendChild(ptDA);
+ css.appendChild(ptEA);
+ css.appendChild(ptSS);
+ css.appendChild(ptV);
+ }
+ else
+ {
+ css = document.createElement("CommonSurfaceShader");
+ css.setAttribute("USE", "MultiMaterial");
+ }
+ appearances[a].replaceChild(css, materials[0]);
+ }
+ else
+ {
+ //Add Material
+ if (firstMat) {
+ firstMat = false;
+ css = document.createElement("CommonSurfaceShader");
+ css.setAttribute("DEF", "MultiMaterial");
+
+ var ptDA = document.createElement("PixelTexture");
+ ptDA.setAttribute("containerField", "multiDiffuseAlphaTexture");
+ ptDA.setAttribute("id", "MultiMaterial_ColorMap");
+ ptDA.setAttribute("image", materialData.diffuseTransparency);
+
+ var ptEA = document.createElement("PixelTexture");
+ ptEA.setAttribute("containerField", "multiEmissiveAmbientTexture");
+ ptEA.setAttribute("id", "MultiMaterial_EmissiveMap");
+ ptEA.setAttribute("image", materialData.emissiveAmbientIntensity);
+
+ var ptSS = document.createElement("PixelTexture");
+ ptSS.setAttribute("containerField", "multiSpecularShininessTexture");
+ ptSS.setAttribute("id", "MultiMaterial_SpecularMap");
+ ptSS.setAttribute("image", materialData.specularShininess);
+
+ var ptV = document.createElement("PixelTexture");
+ ptV.setAttribute("containerField", "multiVisibilityTexture");
+ ptV.setAttribute("id", "MultiMaterial_VisibilityMap");
+ ptV.setAttribute("image", visibilityData);
+
+ css.appendChild(ptDA);
+ css.appendChild(ptEA);
+ css.appendChild(ptSS);
+ css.appendChild(ptV);
+ }
+ else
+ {
+ css = document.createElement("CommonSurfaceShader");
+ css.setAttribute("USE", "MultiMaterial");
+ }
+
+ appearances[a].appendChild(css);
+ }
+ }
+ }
+ else
+ {
+ //Add Appearance
+ appearance = document.createElement("Appearance");
+
+ //Add Material
+ if (firstMat) {
+ firstMat = false;
+ css = document.createElement("CommonSurfaceShader");
+ css.setAttribute("DEF", "MultiMaterial");
+
+ var ptDA = document.createElement("PixelTexture");
+ ptDA.setAttribute("containerField", "multiDiffuseAlphaTexture");
+ ptDA.setAttribute("id", "MultiMaterial_ColorMap");
+ ptDA.setAttribute("image", materialData.diffuseTransparency);
+
+ var ptEA = document.createElement("PixelTexture");
+ ptEA.setAttribute("containerField", "multiEmissiveAmbientTexture");
+ ptEA.setAttribute("id", "MultiMaterial_EmissiveMap");
+ ptEA.setAttribute("image", materialData.emissiveAmbientIntensity);
+
+ var ptSS = document.createElement("PixelTexture");
+ ptSS.setAttribute("containerField", "multiSpecularShininessTexture");
+ ptSS.setAttribute("id", "MultiMaterial_SpecularMap");
+ ptSS.setAttribute("image", materialData.specularShininess);
+
+ var ptV = document.createElement("PixelTexture");
+ ptV.setAttribute("containerField", "multiVisibilityTexture");
+ ptV.setAttribute("id", "MultiMaterial_VisibilityMap");
+ ptV.setAttribute("image", visibilityData);
+
+ css.appendChild(ptDA);
+ css.appendChild(ptEA);
+ css.appendChild(ptSS);
+ css.appendChild(ptV);
+ }
+ else
+ {
+ css = document.createElement("CommonSurfaceShader");
+ css.setAttribute("USE", "MultiMaterial");
+ }
+
+ appearance.appendChild(css);
+ geometries[g].appendChild(appearance);
+ }
+ }
+ }
+ },
+
+ appendAPI: function ()
+ {
+ var multiPart = this;
+
+ this._xmlNode.getIdList = function ()
+ {
+ var i, ids = [];
+
+ for (i=0; i<multiPart._idMap.mapping.length; i++) {
+ ids.push( multiPart._idMap.mapping[i].name );
+ }
+
+ return ids;
+ };
+
+ this._xmlNode.getAppearanceIdList = function ()
+ {
+ var i, ids = [];
+
+ for (i=0; i<multiPart._idMap.appearance.length; i++) {
+ ids.push( multiPart._idMap.appearance[i].name );
+ }
+
+ return ids;
+ };
+
+ this._xmlNode.getParts = function (selector)
+ {
+ var i, m;
+ var selection = [];
+
+ if (selector == undefined) {
+ for (m=0; m<multiPart._idMap.mapping.length; m++) {
+ selection.push(m);
+ }
+ } else {
+ for (i=0; i<selector.length; i++) {
+ if (multiPart._identifierToPartId[selector[i]]) {
+ selection = selection.concat(multiPart._identifierToPartId[selector[i]]);
+ }
+ }
+ }
+
+ var colorMap = multiPart._inlineNamespace.defMap["MultiMaterial_ColorMap"];
+ var emissiveMap = multiPart._inlineNamespace.defMap["MultiMaterial_EmissiveMap"];
+ var specularMap = multiPart._inlineNamespace.defMap["MultiMaterial_SpecularMap"];
+ var visibilityMap = multiPart._inlineNamespace.defMap["MultiMaterial_VisibilityMap"];
+
+ if ( selection.length == 0) {
+ return null;
+ } else {
+ return new x3dom.Parts(multiPart, selection, colorMap, emissiveMap, specularMap, visibilityMap);
+ }
+ };
+ },
+
+ loadInline: function ()
+ {
+ var that = this;
+ var xhr = new window.XMLHttpRequest();
+ if (xhr.overrideMimeType)
+ xhr.overrideMimeType('text/xml'); //application/xhtml+xml
+
+ xhr.onreadystatechange = function ()
+ {
+ if (xhr.readyState != 4) {
+ // still loading
+ //x3dom.debug.logInfo('Loading inlined data... (readyState: ' + xhr.readyState + ')');
+ return xhr;
+ }
+
+ if (xhr.status === x3dom.nodeTypes.Inline.AwaitTranscoding) {
+ if (that.count < that.numRetries)
+ {
+ that.count++;
+ var refreshTime = +xhr.getResponseHeader("Refresh") || 5;
+ x3dom.debug.logInfo('XHR status: ' + xhr.status + ' - Await Transcoding (' + that.count + '/' + that.numRetries + '): ' +
+ 'Next request in ' + refreshTime + ' seconds');
+
+ window.setTimeout(function() {
+ that._nameSpace.doc.downloadCount -= 1;
+ that.loadInline();
+ }, refreshTime * 1000);
+ return xhr;
+ }
+ else
+ {
+ x3dom.debug.logError('XHR status: ' + xhr.status + ' - Await Transcoding (' + that.count + '/' + that.numRetries + '): ' +
+ 'No Retries left');
+ that._nameSpace.doc.downloadCount -= 1;
+ that.count = 0;
+ return xhr;
+ }
+ }
+ else if ((xhr.status !== 200) && (xhr.status !== 0)) {
+ that.fireEvents("error");
+ x3dom.debug.logError('XHR status: ' + xhr.status + ' - XMLHttpRequest requires web server running!');
+
+ that._nameSpace.doc.downloadCount -= 1;
+ that.count = 0;
+ return xhr;
+ }
+ else if ((xhr.status == 200) || (xhr.status == 0)) {
+ that.count = 0;
+ }
+
+ x3dom.debug.logInfo('Inline: downloading '+that._vf.url[0]+' done.');
+
+ var inlScene = null, newScene = null, nameSpace = null, xml = null;
+
+ if (navigator.appName != "Microsoft Internet Explorer")
+ xml = xhr.responseXML;
+ else
+ xml = new DOMParser().parseFromString(xhr.responseText, "text/xml");
+
+ //TODO; check if exists and FIXME: it's not necessarily the first scene in the doc!
+ if (xml !== undefined && xml !== null)
+ {
+ inlScene = xml.getElementsByTagName('Scene')[0] ||
+ xml.getElementsByTagName('scene')[0];
+ }
+ else {
+ that.fireEvents("error");
+ }
+
+ if (inlScene)
+ {
+ var nsDefault = "ns" + that._nameSpace.childSpaces.length;
+
+ var nsName = (that._vf.nameSpaceName.length != 0) ?
+ that._vf.nameSpaceName.toString().replace(' ','') : nsDefault;
+
+ that._inlineNamespace = new x3dom.NodeNameSpace(nsName, that._nameSpace.doc);
+
+ var url = that._vf.url.length ? that._vf.url[0] : "";
+
+ if ((url[0] === '/') || (url.indexOf(":") >= 0))
+ {
+ that._inlineNamespace.setBaseURL(url);
+ }
+ else
+ {
+ that._inlineNamespace.setBaseURL(that._nameSpace.baseURL + url);
+ }
+
+ //Replace Material before setupTree()
+ that.replaceMaterials(inlScene);
+
+ newScene = that._inlineNamespace.setupTree(inlScene);
+
+ that._nameSpace.addSpace(that._inlineNamespace);
+
+ if(that._vf.nameSpaceName.length != 0)
+ {
+ Array.forEach ( inlScene.childNodes, function (childDomNode)
+ {
+ if(childDomNode instanceof Element)
+ {
+ setNamespace(that._vf.nameSpaceName, childDomNode, that._vf.mapDEFToID);
+ that._xmlNode.appendChild(childDomNode);
+ }
+ } );
+ }
+ }
+ else {
+ if (xml && xml.localName) {
+ x3dom.debug.logError('No Scene in ' + xml.localName);
+ } else {
+ x3dom.debug.logError('No Scene in resource');
+ }
+ }
+
+ // trick to free memory, assigning a property to global object, then deleting it
+ var global = x3dom.getGlobal();
+
+ if (that._childNodes.length > 0 && that._childNodes[0] && that._childNodes[0]._nameSpace) {
+ that._nameSpace.removeSpace(that._childNodes[0]._nameSpace);
+ }
+
+ while (that._childNodes.length !== 0) {
+ global['_remover'] = that.removeChild(that._childNodes[0]);
+ }
+
+ delete global['_remover'];
+
+ if (newScene)
+ {
+ that.addChild(newScene);
+
+ that.invalidateVolume();
+ //that.invalidateCache();
+
+ that._nameSpace.doc.downloadCount -= 1;
+ that._nameSpace.doc.needRender = true;
+ x3dom.debug.logInfo('Inline: added ' + that._vf.url[0] + ' to scene.');
+
+ // recalc changed scene bounding box twice
+ var theScene = that._nameSpace.doc._scene;
+
+ if (theScene) {
+ theScene.invalidateVolume();
+ //theScene.invalidateCache();
+
+ window.setTimeout( function() {
+ that.invalidateVolume();
+ //that.invalidateCache();
+
+ theScene.updateVolume();
+ that._nameSpace.doc.needRender = true;
+ }, 1000 );
+ }
+
+ that.appendAPI();
+ that.fireEvents("load");
+ }
+
+ newScene = null;
+ //nameSpace = null;
+ inlScene = null;
+ xml = null;
+
+ return xhr;
+ };
+
+ if (this._vf.url.length && this._vf.url[0].length)
+ {
+ var xhrURI = this._nameSpace.getURL(this._vf.url[0]);
+
+ xhr.open('GET', xhrURI, true);
+
+ this._nameSpace.doc.downloadCount += 1;
+
+ try {
+ xhr.send(null);
+ }
+ catch(ex) {
+ this.fireEvents("error");
+ x3dom.debug.logError(this._vf.url[0] + ": " + ex);
+ }
+ }
+ }
+ }
+ )
+);
+
+/** @namespace x3dom.nodeTypes */
+/*
+ * X3DOM JavaScript Library
+ * http://www.x3dom.org
+ *
+ * (C)2009 Fraunhofer IGD, Darmstadt, Germany
+ * Dual licensed under the MIT and GPL
+ */
+
+x3dom.registerNodeType(
+ "X3DBackgroundNode",
+ "EnvironmentalEffects",
+ defineClass(x3dom.nodeTypes.X3DBindableNode,
+
+ /**
+ * Constructor for X3DBackgroundNode
+ * @constructs x3dom.nodeTypes.X3DBackgroundNode
+ * @x3d 3.3
+ * @component EnvironmentalEffects
+ * @status full
+ * @extends x3dom.nodeTypes.X3DBindableNode
+ * @param {Object} [ctx=null] - context object, containing initial settings like namespace
+ * @classdesc X3DBackgroundNode is the abstract type from which all backgrounds inherit. X3DBackgroundNode is a
+ * bindable node that, when bound, defines the panoramic background for the scene.
+ */
+ function (ctx) {
+ x3dom.nodeTypes.X3DBackgroundNode.superClass.call(this, ctx);
+
+ var trans = (ctx && ctx.autoGen) ? 1 : 0;
+
+ /**
+ * Cross Origin Mode
+ * @var {x3dom.fields.SFString} crossOrigin
+ * @memberof x3dom.nodeTypes.X3DBackgroundNode
+ * @initvalue ""
+ * @field x3d
+ * @instance
+ */
+ this.addField_SFString(ctx, 'crossOrigin', '');
+
+ /**
+ * Color of the ground
+ * @var {x3dom.fields.MFColor} groundColor
+ * @memberof x3dom.nodeTypes.X3DBackgroundNode
+ * @initvalue (0,0,0)
+ * @range [0,1]
+ * @field x3d
+ * @instance
+ */
+ this.addField_MFColor(ctx, 'groundColor', []);
+
+ /**
+ * Angle of the ground
+ * @var {x3dom.fields.MFFloat} groundAngle
+ * @memberof x3dom.nodeTypes.X3DBackgroundNode
+ * @initvalue []
+ * @range [0, pi]
+ * @field x3d
+ * @instance
+ */
+ this.addField_MFFloat(ctx, 'groundAngle', []);
+
+ /**
+ * Color of the sky
+ * @var {x3dom.fields.MFColor} skyColor
+ * @memberof x3dom.nodeTypes.X3DBackgroundNode
+ * @initvalue (0,0,0)
+ * @range [0,1]
+ * @field x3d
+ * @instance
+ */
+ this.addField_MFColor(ctx, 'skyColor', [new x3dom.fields.SFColor(0,0,0)]);
+
+ /**
+ * Angle of the sky
+ * @var {x3dom.fields.MFFloat} skyAngle
+ * @memberof x3dom.nodeTypes.X3DBackgroundNode
+ * @initvalue []
+ * @range [0, pi]
+ * @field x3d
+ * @instance
+ */
+ this.addField_MFFloat(ctx, 'skyAngle', []);
+
+ /**
+ * Transparency of the background
+ * @var {x3dom.fields.SFFloat} transparency
+ * @memberof x3dom.nodeTypes.X3DBackgroundNode
+ * @initvalue 0/1
+ * @range [0,1]
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFFloat(ctx, 'transparency', trans);
+
+ this._dirty = true;
+
+ },
+ {
+ getSkyColor: function() {
+ return new x3dom.fields.SFColor(0,0,0);
+ },
+ getTransparency: function() {
+ return 0;
+ },
+ getTexUrl: function() {
+ return [];
+ }
+ }
+ )
+);
+/** @namespace x3dom.nodeTypes */
+/*
+ * X3DOM JavaScript Library
+ * http://www.x3dom.org
+ *
+ * (C)2009 Fraunhofer IGD, Darmstadt, Germany
+ * Dual licensed under the MIT and GPL
+ */
+
+/* ### X3DFogNode ### */
+x3dom.registerNodeType(
+ "X3DFogNode",
+ "EnvironmentalEffects",
+ defineClass(x3dom.nodeTypes.X3DBindableNode,
+
+ /**
+ * Constructor for X3DFogNode
+ * @constructs x3dom.nodeTypes.X3DFogNode
+ * @x3d 3.3
+ * @component EnvironmentalEffects
+ * @status full
+ * @extends x3dom.nodeTypes.X3DBindableNode
+ * @param {Object} [ctx=null] - context object, containing initial settings like namespace
+ * @classdesc X3DFogObject is the abstract type that describes a node that influences the lighting equation
+ * through the use of fog semantics.
+ */
+ function (ctx) {
+ x3dom.nodeTypes.X3DFogNode.superClass.call(this, ctx);
+
+ /**
+ * Objects located outside the visibilityRange from the viewer are drawn with a constant colour of color.
+ * Objects very close to the viewer are blended very little with the fog color.
+ * @var {x3dom.fields.SFColor} color
+ * @memberof x3dom.nodeTypes.X3DFogNode
+ * @initvalue (1,1,1)
+ * @range [0,1]
+ * @field x3d
+ * @instance
+ */
+ this.addField_SFColor(ctx, 'color', 1, 1, 1);
+
+ /**
+ * The fogType field controls how much of the fog colour is blended with the object as a function of
+ * distance. If fogType is "LINEAR", the amount of blending is a linear function of the distance, resulting
+ * in a depth cueing effect. If fogType is "EXPONENTIAL," an exponential increase in blending is used,
+ * resulting in a more natural fog appearance.
+ * @var {x3dom.fields.SFString} fogType
+ * @memberof x3dom.nodeTypes.X3DFogNode
+ * @initvalue "LINEAR"
+ * @range {"LINEAR","EXPONENTIAL"}
+ * @field x3d
+ * @instance
+ */
+ this.addField_SFString(ctx, 'fogType', "LINEAR");
+
+ /**
+ * The visibilityRange specifies the distance in length base units (in the local coordinate system) at
+ * which objects are totally obscured by the fog. A visibilityRange of 0.0 disables the Fog node.
+ * The visibilityRange is affected by the scaling transformations of the Fog node's parents; translations
+ * and rotations have no affect on visibilityRange.
+ * @var {x3dom.fields.SFFloat} visibilityRange
+ * @memberof x3dom.nodeTypes.X3DFogNode
+ * @initvalue 0
+ * @range [0, -inf]
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFFloat(ctx, 'visibilityRange', 0);
+
+ },
+ {
+ }
+ )
+);
+/** @namespace x3dom.nodeTypes */
+/*
+ * X3DOM JavaScript Library
+ * http://www.x3dom.org
+ *
+ * (C)2009 Fraunhofer IGD, Darmstadt, Germany
+ * Dual licensed under the MIT and GPL
+ */
+
+/* ### Fog ### */
+x3dom.registerNodeType(
+ "Fog",
+ "EnvironmentalEffects",
+ defineClass(x3dom.nodeTypes.X3DFogNode,
+
+ /**
+ * Constructor for Fog
+ * @constructs x3dom.nodeTypes.Fog
+ * @x3d 3.3
+ * @component EnvironmentalEffects
+ * @status experimental
+ * @extends x3dom.nodeTypes.X3DFogNode
+ * @param {Object} [ctx=null] - context object, containing initial settings like namespace
+ * @classdesc The Fog node provides a way to simulate atmospheric effects by blending objects with the colour
+ * specified by the color field based on the distances of the various objects from the viewer. The distances
+ * are calculated in the coordinate space of the Fog node.
+ */
+ function (ctx) {
+ x3dom.nodeTypes.Fog.superClass.call(this, ctx);
+
+ },
+ {
+ }
+ )
+);
+/** @namespace x3dom.nodeTypes */
+/*
+ * X3DOM JavaScript Library
+ * http://www.x3dom.org
+ *
+ * (C)2009 Fraunhofer IGD, Darmstadt, Germany
+ * Dual licensed under the MIT and GPL
+ */
+
+/* ### Background ### */
+x3dom.registerNodeType(
+ "Background",
+ "EnvironmentalEffects",
+ defineClass(x3dom.nodeTypes.X3DBackgroundNode,
+
+ /**
+ * Constructor for Background
+ * @constructs x3dom.nodeTypes.Background
+ * @x3d 3.3
+ * @component EnvironmentalEffects
+ * @status full
+ * @extends x3dom.nodeTypes.X3DBackgroundNode
+ * @param {Object} [ctx=null] - context object, containing initial settings like namespace
+ * @classdesc A background node that uses six static images to compose the backdrop. For the backUrl,
+ * bottomUrl, frontUrl, leftUrl, rightUrl, topUrl fields, browsers shall support the JPEG and PNG
+ * (see ISO/IEC 15948) image file formats.
+ */
+ function (ctx) {
+ x3dom.nodeTypes.Background.superClass.call(this, ctx);
+
+ /**
+ *
+ * @var {x3dom.fields.MFString} backUrl
+ * @memberof x3dom.nodeTypes.Background
+ * @initvalue []
+ * @range [URI]
+ * @field x3d
+ * @instance
+ */
+ this.addField_MFString(ctx, 'backUrl', []);
+
+ /**
+ *
+ * @var {x3dom.fields.MFString} bottomUrl
+ * @memberof x3dom.nodeTypes.Background
+ * @initvalue []
+ * @range [URI]
+ * @field x3dom
+ * @instance
+ */
+ this.addField_MFString(ctx, 'bottomUrl', []);
+
+ /**
+ *
+ * @var {x3dom.fields.MFString} frontUrl
+ * @memberof x3dom.nodeTypes.Background
+ * @initvalue []
+ * @range [URI]
+ * @field x3dom
+ * @instance
+ */
+ this.addField_MFString(ctx, 'frontUrl', []);
+
+ /**
+ *
+ * @var {x3dom.fields.MFString} leftUrl
+ * @memberof x3dom.nodeTypes.Background
+ * @initvalue []
+ * @range [URI]
+ * @field x3dom
+ * @instance
+ */
+ this.addField_MFString(ctx, 'leftUrl', []);
+
+ /**
+ *
+ * @var {x3dom.fields.MFString} rightUrl
+ * @memberof x3dom.nodeTypes.Background
+ * @initvalue []
+ * @range [URI]
+ * @field x3dom
+ * @instance
+ */
+ this.addField_MFString(ctx, 'rightUrl', []);
+
+ /**
+ *
+ * @var {x3dom.fields.MFString} topUrl
+ * @memberof x3dom.nodeTypes.Background
+ * @initvalue []
+ * @range [URI]
+ * @field x3dom
+ * @instance
+ */
+ this.addField_MFString(ctx, 'topUrl', []);
+
+ },
+ {
+ fieldChanged: function(fieldName)
+ {
+ if (fieldName.indexOf("Url") > 0 || fieldName == "transparency" ||
+ fieldName.search("sky") >= 0 || fieldName.search("ground") >= 0) {
+ this._dirty = true;
+ }
+ else if (fieldName.indexOf("bind") >= 0) {
+ this.bind(this._vf.bind);
+ }
+ },
+
+ getSkyColor: function() {
+ return this._vf.skyColor;
+ },
+
+ getGroundColor: function() {
+ return this._vf.groundColor;
+ },
+
+ getTransparency: function() {
+ return this._vf.transparency;
+ },
+
+ getTexUrl: function() {
+ return [
+ this._nameSpace.getURL(this._vf.backUrl[0]),
+ this._nameSpace.getURL(this._vf.frontUrl[0]),
+ this._nameSpace.getURL(this._vf.bottomUrl[0]),
+ this._nameSpace.getURL(this._vf.topUrl[0]),
+ this._nameSpace.getURL(this._vf.leftUrl[0]),
+ this._nameSpace.getURL(this._vf.rightUrl[0])
+ ];
+ }
+ }
+ )
+);
+/** @namespace x3dom.nodeTypes */
+/*
+ * X3DOM JavaScript Library
+ * http://www.x3dom.org
+ *
+ * (C)2009 Fraunhofer IGD, Darmstadt, Germany
+ * Dual licensed under the MIT and GPL
+ */
+
+/* ### X3DEnvironmentNode ### */
+x3dom.registerNodeType(
+ "X3DEnvironmentNode",
+ "EnvironmentalEffects",
+ defineClass(x3dom.nodeTypes.X3DBindableNode,
+
+ /**
+ * Constructor for X3DEnvironmentNode
+ * @constructs x3dom.nodeTypes.X3DEnvironmentNode
+ * @x3d x.x
+ * @component EnvironmentalEffects
+ * @status full
+ * @extends x3dom.nodeTypes.X3DBindableNode
+ * @param {Object} [ctx=null] - context object, containing initial settings like namespace
+ * @classdesc Base class for environment nodes
+ */
+ function (ctx) {
+ x3dom.nodeTypes.X3DEnvironmentNode.superClass.call(this, ctx);
+
+ }
+ )
+);
+/** @namespace x3dom.nodeTypes */
+/*
+ * X3DOM JavaScript Library
+ * http://www.x3dom.org
+ *
+ * (C)2009 Fraunhofer IGD, Darmstadt, Germany
+ * Dual licensed under the MIT and GPL
+ */
+
+/* ### Environment ### */
+x3dom.registerNodeType(
+ "Environment",
+ "EnvironmentalEffects",
+ defineClass(x3dom.nodeTypes.X3DEnvironmentNode,
+
+ /**
+ * Constructor for Environment
+ * @constructs x3dom.nodeTypes.Environment
+ * @x3d x.x
+ * @component EnvironmentalEffects
+ * @status full
+ * @extends x3dom.nodeTypes.X3DEnvironmentNode
+ * @param {Object} [ctx=null] - context object, containing initial settings like namespace
+ * @classdesc Bindable node to setup rendering and culling parameters
+ */
+ function (ctx) {
+ x3dom.nodeTypes.Environment.superClass.call(this, ctx);
+
+ /**
+ * If TRUE, transparent objects are sorted from back to front (allows explicitly disabling sorting)
+ * @var {x3dom.fields.SFBool} sortTrans
+ * @memberof x3dom.nodeTypes.Environment
+ * @initvalue true
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFBool(ctx, 'sortTrans', true);
+
+ /**
+ * Transparent objects like glass do not throw much shadow, enable this IR convenience flag with TRUE
+ * @var {x3dom.fields.SFBool} shadowExcludeTransparentObjects
+ * @memberof x3dom.nodeTypes.Environment
+ * @initvalue false
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFBool(ctx, 'shadowExcludeTransparentObjects', false);
+
+ /**
+ * The gamma correction to apply by default, see lighting and gamma tutorial
+ * @var {x3dom.fields.SFString} gammaCorrectionDefault
+ * @memberof x3dom.nodeTypes.Environment
+ * @initvalue "none"
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFString(ctx, 'gammaCorrectionDefault', "linear");
+
+ // boolean flags for feature (de)activation
+
+ /**
+ * If TRUE, objects outside the viewing frustum are ignored
+ * @var {x3dom.fields.SFBool} frustumCulling
+ * @memberof x3dom.nodeTypes.Environment
+ * @initvalue true
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFBool(ctx, 'frustumCulling', true);
+
+ /**
+ * If TRUE, objects smaller than the threshold below are ignored
+ * @var {x3dom.fields.SFBool} smallFeatureCulling
+ * @memberof x3dom.nodeTypes.Environment
+ * @initvalue false
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFBool(ctx, 'smallFeatureCulling', false);
+
+ /**
+ * Objects smaller than the threshold below are ignored
+ * @var {x3dom.fields.SFFloat} smallFeatureThreshold
+ * @memberof x3dom.nodeTypes.Environment
+ * @initvalue 1.0
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFFloat(ctx, 'smallFeatureThreshold', 1.0);
+
+ // defaults can be >0 since only used upon activation
+
+ /**
+ * If TRUE and occlusion culling supported, objects occluding less than the threshold below are ignored
+ * @var {x3dom.fields.SFBool} occlusionCulling
+ * @memberof x3dom.nodeTypes.Environment
+ * @initvalue false
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFBool(ctx, 'occlusionCulling', false);
+
+ /**
+ * Objects occluding less than the threshold below are ignored
+ * @var {x3dom.fields.SFFloat} occlusionVisibilityThreshold
+ * @memberof x3dom.nodeTypes.Environment
+ * @initvalue 0.0
+ * @range [0,1]
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFFloat(ctx, 'occlusionVisibilityThreshold', 0.0);
+
+ // previously was scaleRenderedIdsOnMove; percentage of objects to be rendered, in [0,1]
+
+ /**
+ * If TRUE and occlusion culling supported, only threshold fraction of objects, sorted by their screen
+ * space coverage, are rendered
+ * @var {x3dom.fields.SFBool} lowPriorityCulling
+ * @memberof x3dom.nodeTypes.Environment
+ * @initvalue false
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFBool(ctx, 'lowPriorityCulling', false);
+
+ /**
+ * Only threshold fraction of objects, sorted by their screen space coverage, are rendered
+ * @var {x3dom.fields.SFFloat} lowPriorityThreshold
+ * @memberof x3dom.nodeTypes.Environment
+ * @initvalue 1.0
+ * @range [0,1]
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFFloat(ctx, 'lowPriorityThreshold', 1.0); // 1.0 means everything is rendered
+
+ // shape tesselation is lowered as long as resulting error is lower than threshold
+
+ /**
+ * If TRUE, shape tesselation is lowered as long as resulting error is lower than threshold
+ * @var {x3dom.fields.SFBool} tessellationDetailCulling
+ * @memberof x3dom.nodeTypes.Environment
+ * @initvalue false
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFBool(ctx, 'tessellationDetailCulling', false);
+
+ /**
+ * Shape tesselation is lowered as long as resulting error is lower than threshold
+ * @var {x3dom.fields.SFFloat} tessellationErrorThreshold
+ * @memberof x3dom.nodeTypes.Environment
+ * @initvalue 0.0
+ * @range [0,1]
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFFloat(ctx, 'tessellationErrorThreshold', 0.0);
+
+ /**
+ * Experimental: If true ARC adjusts rendering parameters
+ * @var {x3dom.fields.SFBool} enableARC
+ * @memberof x3dom.nodeTypes.Environment
+ * @initvalue false
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFBool(ctx, 'enableARC', false);
+
+ /**
+ * Experimental: Define minimal target frame-rate for static moments and quality-speed trade-off
+ * @var {x3dom.fields.SFFloat} minFrameRate
+ * @memberof x3dom.nodeTypes.Environment
+ * @initvalue 1.0
+ * @range [1,inf]
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFFloat(ctx, 'minFrameRate', 1.0);
+
+ /**
+ * Experimental: Define maximal target frame-rate for dynamic moments and quality-speed trade-off
+ * @var {x3dom.fields.SFFloat} maxFrameRate
+ * @memberof x3dom.nodeTypes.Environment
+ * @initvalue 62.5
+ * @range [1,inf]
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFFloat(ctx, 'maxFrameRate', 62.5);
+
+ // 4 exp. factors for controlling speed-performance trade-off
+ // factors could be in [0, 1] (and not evaluated if -1)
+
+ /**
+ * Experimenal: Factor of user data for controlling speed-performance trade-off
+ * @var {x3dom.fields.SFFloat} userDataFactor
+ * @memberof x3dom.nodeTypes.Environment
+ * @initvalue -1
+ * @range [0,1] or -1
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFFloat(ctx, 'userDataFactor', -1);
+
+ /**
+ * Experimenal: Factor of small feature culling for controlling speed-performance trade-off
+ * @var {x3dom.fields.SFFloat} smallFeatureFactor
+ * @memberof x3dom.nodeTypes.Environment
+ * @initvalue -1
+ * @range [0,1] or -1
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFFloat(ctx, 'smallFeatureFactor', -1);
+
+ /**
+ * Experimenal: Factor of occlusion culling for controlling speed-performance trade-off
+ * @var {x3dom.fields.SFFloat} occlusionVisibilityFactor
+ * @memberof x3dom.nodeTypes.Environment
+ * @initvalue -1
+ * @range [0,1] or -1
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFFloat(ctx, 'occlusionVisibilityFactor', -1);
+
+ /**
+ * Experimenal: Factor of low priority culling for controlling speed-performance trade-off
+ * @var {x3dom.fields.SFFloat} lowPriorityFactor
+ * @memberof x3dom.nodeTypes.Environment
+ * @initvalue -1
+ * @range [0,1] or -1
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFFloat(ctx, 'lowPriorityFactor', -1);
+
+ /**
+ * Experimenal: Factor of tesselation error for controlling speed-performance trade-off
+ * @var {x3dom.fields.SFFloat} tessellationErrorFactor
+ * @memberof x3dom.nodeTypes.Environment
+ * @initvalue -1
+ * @range [0,1] or -1
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFFloat(ctx, 'tessellationErrorFactor', -1);
+
+ /**
+ * Flag to enable Screen Space Ambient Occlusion
+ * @var {x3dom.fields.SFBool} SSAO
+ * @memberof x3dom.nodeTypes.Environment
+ * @initvalue "false"
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFBool(ctx, 'SSAO', false);
+
+ /**
+ * Value that determines the radius in which the SSAO is sampled in world space
+ * @var {x3dom.fields.SFFloat} SSAOradius
+ * @memberof x3dom.nodeTypes.Environment
+ * @initvalue "4"
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFFloat(ctx, 'SSAOradius',0.7);
+
+ /**
+ * Value that determines the amount of contribution of SSAO (from 0 to 1)
+ * @var {x3dom.fields.SFFloat} SSAOamount
+ * @memberof x3dom.nodeTypes.Environment
+ * @initvalue "1.0"
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFFloat(ctx, 'SSAOamount',0.3);
+
+ /**
+ * Value that determines the size of the random texture used for sparse sampling of SSAO
+ * @var {x3dom.fields.SFFloat} SSAOrandomTextureSize
+ * @memberof x3dom.nodeTypes.Environment
+ * @initvalue "4"
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFInt32(ctx, 'SSAOrandomTextureSize',4);
+
+ /**
+ * Value that determines the maximum depth difference for the SSAO blurring pass.
+ * Pixels with higher depth difference to the filer kernel center are not incorporated into the average.
+ * @var {x3dom.fields.SFFloat} SSAOblurDepthTreshold
+ * @memberof x3dom.nodeTypes.Environment
+ * @initvalue "5"
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFInt32(ctx, 'SSAOblurDepthTreshold',1);
+
+ this._validGammaCorrectionTypes = [
+ "none", "fastlinear", "linear"
+ ];
+
+ // init internal stuff (but should be called each frame)
+ this.checkSanity();
+
+ },
+ {
+ checkSanity: function()
+ {
+ var checkParam = function(flag, value, defaultOn, defaultOff)
+ {
+ if(flag && (value == defaultOff))
+ return defaultOn;
+
+ if(!flag && (value != defaultOff))
+ return defaultOff;
+ return value;
+ };
+
+ this._smallFeatureThreshold = checkParam(this._vf.smallFeatureCulling,
+ this._vf.smallFeatureThreshold, 10, 0); // cull objects < 10 px
+ this._lowPriorityThreshold = checkParam(this._vf.lowPriorityCulling,
+ this._vf.lowPriorityThreshold, 0.5, 1); // 1 means 100% visible
+ this._occlusionVisibilityThreshold = checkParam(this._vf.occlusionCulling,
+ this._vf.occlusionVisibilityThreshold, 1, 0);
+ this._tessellationErrorThreshold = checkParam(this._vf.tessellationDetailCulling,
+ this._vf.tessellationErrorThreshold, 1, 0);
+
+ var checkGamma = function(field, that) {
+ field = field.toLowerCase();
+
+ if (that._validGammaCorrectionTypes.indexOf(field) > -1) {
+ return field;
+ }
+ else {
+ x3dom.debug.logWarning(field + " gammaCorrectionDefault may only be linear (default), fastLinear, or none");
+ return that._validGammaCorrectionTypes[0];
+ }
+ };
+
+ this._vf.gammaCorrectionDefault = checkGamma(this._vf.gammaCorrectionDefault, this);
+ }
+ }
+ )
+);
+
+/** @namespace x3dom.nodeTypes */
+/*
+ * X3DOM JavaScript Library
+ * http://www.x3dom.org
+ *
+ * (C)2009 Fraunhofer IGD, Darmstadt, Germany
+ * Dual licensed under the MIT and GPL
+ */
+
+/* ### X3DViewpointNode ### */
+x3dom.registerNodeType(
+ "X3DViewpointNode",
+ "Navigation",
+ defineClass(x3dom.nodeTypes.X3DBindableNode,
+
+ /**
+ * Constructor for X3DViewpointNode
+ * @constructs x3dom.nodeTypes.X3DViewpointNode
+ * @x3d 3.3
+ * @component Navigation
+ * @status experimental
+ * @extends x3dom.nodeTypes.X3DBindableNode
+ * @param {Object} [ctx=null] - context object, containing initial settings like namespace
+ * @classdesc A node of node type X3DViewpointNode defines a specific location in the local coordinate system from which the user may view the scene.
+ */
+ function (ctx) {
+ x3dom.nodeTypes.X3DViewpointNode.superClass.call(this, ctx);
+
+ // attach some convenience accessor methods to dom/xml node
+ if (ctx && ctx.xmlNode) {
+ var domNode = ctx.xmlNode;
+
+ if (!domNode.resetView && !domNode.getFieldOfView &&
+ !domNode.getNear && !domNode.getFar)
+ {
+ domNode.resetView = function() {
+ var that = this._x3domNode;
+
+ that.resetView();
+ that._nameSpace.doc.needRender = true;
+ };
+
+ domNode.getFieldOfView = function() {
+ return this._x3domNode.getFieldOfView();
+ };
+
+ domNode.getNear = function() {
+ return this._x3domNode.getNear();
+ };
+
+ domNode.getFar = function() {
+ return this._x3domNode.getFar();
+ };
+ }
+ }
+
+ },
+ {
+ activate: function (prev) {
+ var viewarea = this._nameSpace.doc._viewarea;
+ if (prev) {
+ viewarea.animateTo(this, prev._autoGen ? null : prev);
+ }
+ viewarea._needNavigationMatrixUpdate = true;
+
+ x3dom.nodeTypes.X3DBindableNode.prototype.activate.call(this, prev);
+ //x3dom.debug.logInfo ('activate ViewBindable ' + this._DEF + '/' + this._vf.description);
+ },
+
+ deactivate: function (prev) {
+ x3dom.nodeTypes.X3DBindableNode.prototype.deactivate.call(this, prev);
+ //x3dom.debug.logInfo ('deactivate ViewBindable ' + this._DEF + '/' + this._vf.description);
+ },
+
+ getTransformation: function() {
+ return this.getCurrentTransform();
+ },
+
+ getCenterOfRotation: function() {
+ return new x3dom.fields.SFVec3f(0, 0, 0);
+ },
+
+ setCenterOfRotation: function(cor) {
+ this._vf.centerOfRotation.setValues(cor); // method overwritten by Viewfrustum
+ },
+
+ getFieldOfView: function() {
+ return 1.57079633;
+ },
+
+ /**
+ * Sets the (local) view matrix
+ * @param newView
+ */
+ setView: function(newView) {
+ var mat = this.getCurrentTransform();
+ this._viewMatrix = newView.mult(mat);
+ },
+
+ /**
+ * Sets an absolute view matrix in world coordinates
+ * @param newView
+ */
+ setViewAbsolute: function(newView)
+ {
+ this._viewMatrix = newView
+ },
+
+ setProjectionMatrix: function(matrix)
+ {
+
+ },
+
+ resetView: function() {
+ // see derived class
+ },
+
+ getNear: function() {
+ return 0.1;
+ },
+
+ getFar: function() {
+ return 10000;
+ },
+
+ getImgPlaneHeightAtDistOne: function() {
+ return 2.0;
+ },
+
+ getViewMatrix: function() {
+ return null;
+ },
+
+ getProjectionMatrix: function(aspect) {
+ return null;
+ }
+ }
+ )
+);
+/** @namespace x3dom.nodeTypes */
+/*
+ * X3DOM JavaScript Library
+ * http://www.x3dom.org
+ *
+ * (C)2009 Fraunhofer IGD, Darmstadt, Germany
+ * Dual licensed under the MIT and GPL
+ */
+
+/* ### Viewpoint ### */
+x3dom.registerNodeType(
+ "Viewpoint",
+ "Navigation",
+ defineClass(x3dom.nodeTypes.X3DViewpointNode,
+
+ /**
+ * Constructor for Viewpoint
+ * @constructs x3dom.nodeTypes.Viewpoint
+ * @x3d 3.3
+ * @component Navigation
+ * @status experimental
+ * @extends x3dom.nodeTypes.X3DViewpointNode
+ * @param {Object} [ctx=null] - context object, containing initial settings like namespace
+ * @classdesc Viewpoint provides a specific location and direction where the user may view the scene.
+ * The principalPoint extention allows to set asymmetric frustums.
+ */
+ function (ctx) {
+ x3dom.nodeTypes.Viewpoint.superClass.call(this, ctx);
+
+
+ /**
+ * Preferred minimum viewing angle from this viewpoint in radians.
+ * Small field of view roughly corresponds to a telephoto lens, large field of view roughly corresponds to a wide-angle lens.
+ * Hint: modifying Viewpoint distance to object may be better for zooming.
+ * Warning: fieldOfView may not be correct for different window sizes and aspect ratios.
+ * Interchange profile hint: this field may be ignored.
+ * @var {x3dom.fields.SFFloat} fieldOfView
+ * @range [0, pi]
+ * @memberof x3dom.nodeTypes.Viewpoint
+ * @initvalue 0.785398
+ * @field x3d
+ * @instance
+ */
+ this.addField_SFFloat(ctx, 'fieldOfView', 0.785398);
+
+ /**
+ * The position fields of the Viewpoint node specifies a relative location in the local coordinate system. Position is relative to the coordinate system's origin (0,0,0),
+ * @var {x3dom.fields.SFVec3f} position
+ * @memberof x3dom.nodeTypes.Viewpoint
+ * @initvalue 0,0,10
+ * @field x3d
+ * @instance
+ */
+ this.addField_SFVec3f(ctx, 'position', 0, 0, 10);
+
+ /**
+ * The orientation fields of the Viewpoint node specifies relative orientation to the default orientation.
+ * @var {x3dom.fields.SFRotation} orientation
+ * @memberof x3dom.nodeTypes.Viewpoint
+ * @initvalue 0,0,0,1
+ * @field x3d
+ * @instance
+ */
+ this.addField_SFRotation(ctx, 'orientation', 0, 0, 0, 1);
+
+ /**
+ * The centerOfRotation field specifies a center about which to rotate the user's eyepoint when in EXAMINE mode.
+ * @var {x3dom.fields.SFVec3f} centerOfRotation
+ * @memberof x3dom.nodeTypes.Viewpoint
+ * @initvalue 0,0,0
+ * @field x3d
+ * @instance
+ */
+ this.addField_SFVec3f(ctx, 'centerOfRotation', 0, 0, 0);
+
+ /**
+ * Specifies the near plane.
+ * @var {x3dom.fields.SFFloat} zNear
+ * @range -1 or [0, inf]
+ * @memberof x3dom.nodeTypes.Viewpoint
+ * @initvalue -1
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFFloat(ctx, 'zNear', -1); //0.1);
+
+ /**
+ * Specifies the far plane.
+ * @var {x3dom.fields.SFFloat} zFar
+ * @range -1 or [0, inf]
+ * @memberof x3dom.nodeTypes.Viewpoint
+ * @initvalue -1
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFFloat(ctx, 'zFar', -1); //100000);
+
+ //this._viewMatrix = this._vf.orientation.toMatrix().transpose().
+ // mult(x3dom.fields.SFMatrix4f.translation(this._vf.position.negate()));
+ this._viewMatrix = x3dom.fields.SFMatrix4f.translation(this._vf.position).
+ mult(this._vf.orientation.toMatrix()).inverse();
+
+ this._projMatrix = null;
+ this._lastAspect = 1.0;
+
+ // z-ratio: a value around 5000 would be better...
+ this._zRatio = 10000;
+ this._zNear = this._vf.zNear;
+ this._zFar = this._vf.zFar;
+
+ // special stuff...
+ this._imgPlaneHeightAtDistOne = 2.0 * Math.tan(this._vf.fieldOfView / 2.0);
+ },
+ {
+ fieldChanged: function (fieldName) {
+ if (fieldName == "position" || fieldName == "orientation") {
+ this.resetView();
+ }
+ else if (fieldName == "fieldOfView" ||
+ fieldName == "zNear" || fieldName == "zFar") {
+ this._projMatrix = null; // only trigger refresh
+ this._zNear = this._vf.zNear;
+ this._zFar = this._vf.zFar;
+ this._imgPlaneHeightAtDistOne = 2.0 * Math.tan(this._vf.fieldOfView / 2.0);
+ }
+ else if (fieldName.indexOf("bind") >= 0) {
+ // FIXME; call parent.fieldChanged();
+ this.bind(this._vf.bind);
+ }
+ },
+
+ setProjectionMatrix: function(matrix)
+ {
+ this._projMatrix = matrix;
+ },
+
+ getCenterOfRotation: function() {
+ return this._vf.centerOfRotation;
+ },
+
+ getViewMatrix: function() {
+ return this._viewMatrix;
+ },
+
+ getFieldOfView: function() {
+ return this._vf.fieldOfView;
+ },
+
+ resetView: function() {
+ this._viewMatrix = x3dom.fields.SFMatrix4f.translation(this._vf.position).
+ mult(this._vf.orientation.toMatrix()).inverse();
+ },
+
+ getNear: function() {
+ return this._zNear;
+ },
+
+ getFar: function() {
+ return this._zFar;
+ },
+
+ getImgPlaneHeightAtDistOne: function() {
+ return this._imgPlaneHeightAtDistOne;
+ },
+
+ getProjectionMatrix: function(aspect)
+ {
+ var fovy = this._vf.fieldOfView;
+ var zfar = this._vf.zFar;
+ var znear = this._vf.zNear;
+
+ if (znear <= 0 || zfar <= 0)
+ {
+ var nearScale = 0.8, farScale = 1.2;
+ var viewarea = this._nameSpace.doc._viewarea;
+ var scene = viewarea._scene;
+
+ // Doesn't work if called e.g. from RenderedTexture with different sub-scene
+ var min = x3dom.fields.SFVec3f.copy(scene._lastMin);
+ var max = x3dom.fields.SFVec3f.copy(scene._lastMax);
+
+ var dia = max.subtract(min);
+ var sRad = dia.length() / 2;
+
+ var mat = viewarea.getViewMatrix().inverse();
+ var vp = mat.e3();
+
+ // account for scales around the viewpoint
+ var translation = new x3dom.fields.SFVec3f(0,0,0),
+ scaleFactor = new x3dom.fields.SFVec3f(1,1,1);
+ var rotation = new x3dom.fields.Quaternion(0,0,1,0),
+ scaleOrientation = new x3dom.fields.Quaternion(0,0,1,0);
+
+ // unfortunately, decompose is a rather expensive operation
+ mat.getTransform(translation, rotation, scaleFactor, scaleOrientation);
+
+ var minScal = scaleFactor.x, maxScal = scaleFactor.x;
+
+ if (maxScal < scaleFactor.y) maxScal = scaleFactor.y;
+ if (minScal > scaleFactor.y) minScal = scaleFactor.y;
+ if (maxScal < scaleFactor.z) maxScal = scaleFactor.z;
+ if (minScal > scaleFactor.z) minScal = scaleFactor.z;
+
+ if (maxScal > 1)
+ nearScale /= maxScal;
+ else if (minScal > x3dom.fields.Eps && minScal < 1)
+ farScale /= minScal;
+ // near/far scale adaption done
+
+ var sCenter = min.add(dia.multiply(0.5));
+ var vDist = (vp.subtract(sCenter)).length();
+
+ if (sRad) {
+ if (vDist > sRad)
+ znear = (vDist - sRad) * nearScale; // Camera outside scene
+ else
+ znear = 0; // Camera inside scene
+
+ zfar = (vDist + sRad) * farScale;
+ }
+ else {
+ znear = 0.1;
+ zfar = 100000;
+ }
+
+ var zNearLimit = zfar / this._zRatio;
+ znear = Math.max(znear, Math.max(x3dom.fields.Eps, zNearLimit));
+
+ if (zfar > this._vf.zNear && this._vf.zNear > 0)
+ znear = this._vf.zNear;
+ if (this._vf.zFar > znear)
+ zfar = this._vf.zFar;
+
+ if (zfar <= znear)
+ zfar = znear + 1;
+ //x3dom.debug.logInfo("near: " + znear + " -> far:" + zfar);
+ }
+
+ if (this._projMatrix == null)
+ {
+ this._projMatrix = x3dom.fields.SFMatrix4f.perspective(fovy, aspect, znear, zfar);
+ }
+ else if (this._zNear != znear || this._zFar != zfar)
+ {
+ var div = znear - zfar;
+ this._projMatrix._22 = (znear + zfar) / div;
+ this._projMatrix._23 = 2 * znear * zfar / div;
+ }
+ else if (this._lastAspect != aspect)
+ {
+ this._projMatrix._00 = (1 / Math.tan(fovy / 2)) / aspect;
+ this._lastAspect = aspect;
+ }
+
+ // also needed for being able to ask for near and far
+ this._zNear = znear;
+ this._zFar = zfar;
+
+ return this._projMatrix;
+ }
+ }
+ )
+);
+/** @namespace x3dom.nodeTypes */
+/*
+ * X3DOM JavaScript Library
+ * http://www.x3dom.org
+ *
+ * (C)2009 Fraunhofer IGD, Darmstadt, Germany
+ * Dual licensed under the MIT and GPL
+ */
+
+/* ### OrthoViewpoint ### */
+x3dom.registerNodeType(
+ "OrthoViewpoint",
+ "Navigation",
+ defineClass(x3dom.nodeTypes.X3DViewpointNode,
+
+ /**
+ * Constructor for OrthoViewpoint
+ * @constructs x3dom.nodeTypes.OrthoViewpoint
+ * @x3d 3.3
+ * @component Navigation
+ * @status experimental
+ * @extends x3dom.nodeTypes.X3DViewpointNode
+ * @param {Object} [ctx=null] - context object, containing initial settings like namespace
+ * @classdesc The OrthoViewpoint node defines a viewpoint that provides an orthographic view of the scene.
+ * An orthographic view is one in which all projectors are parallel to the projector from centerOfRotation to position.
+ */
+ function (ctx) {
+ x3dom.nodeTypes.OrthoViewpoint.superClass.call(this, ctx);
+
+
+ /**
+ * The fieldOfView field specifies minimum and maximum extents of the view in units of the local coordinate system
+ * @var {x3dom.fields.MFFloat} fieldOfView
+ * @memberof x3dom.nodeTypes.OrthoViewpoint
+ * @initvalue [-1,-1,1,1]
+ * @field x3d
+ * @instance
+ */
+ this.addField_MFFloat(ctx, 'fieldOfView', [-1, -1, 1, 1]);
+
+ /**
+ * Position (x, y, z in meters) relative to local coordinate system.
+ * @var {x3dom.fields.SFVec3f} position
+ * @memberof x3dom.nodeTypes.OrthoViewpoint
+ * @initvalue 0,0,10
+ * @field x3d
+ * @instance
+ */
+ this.addField_SFVec3f(ctx, 'position', 0, 0, 10);
+
+ /**
+ * Rotation (axis, angle in radians) of Viewpoint, relative to default -Z axis direction in local coordinate system.
+ * Hint: this is orientation _change_ from default direction (0 0 -1).
+ * Hint: complex rotations can be accomplished axis-by-axis using parent Transforms.
+ * @var {x3dom.fields.SFRotation} orientation
+ * @range [-1, 1] or [-inf, inf]
+ * @memberof x3dom.nodeTypes.OrthoViewpoint
+ * @initvalue 0,0,0,1
+ * @field x3d
+ * @instance
+ */
+ this.addField_SFRotation(ctx, 'orientation', 0, 0, 0, 1);
+
+ /**
+ * centerOfRotation point relates to NavigationInfo EXAMINE mode.
+ * @var {x3dom.fields.SFVec3f} centerOfRotation
+ * @memberof x3dom.nodeTypes.OrthoViewpoint
+ * @initvalue 0,0,0
+ * @field x3d
+ * @instance
+ */
+ this.addField_SFVec3f(ctx, 'centerOfRotation', 0, 0, 0);
+
+ /**
+ * z-near position; used for clipping
+ * @var {x3dom.fields.SFFloat} zNear
+ * @memberof x3dom.nodeTypes.OrthoViewpoint
+ * @initvalue 0.1
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFFloat(ctx, 'zNear', 0.1);
+
+ /**
+ * z-far position; used for clipping
+ * @var {x3dom.fields.SFFloat} zFar
+ * @memberof x3dom.nodeTypes.OrthoViewpoint
+ * @initvalue 10000
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFFloat(ctx, 'zFar', 10000);
+
+ this._viewMatrix = null;
+ this._projMatrix = null;
+ this._lastAspect = 1.0;
+
+ this.resetView();
+
+ },
+ {
+ fieldChanged: function (fieldName) {
+ if (fieldName == "position" || fieldName == "orientation") {
+ this.resetView();
+ }
+ else if (fieldName == "fieldOfView" ||
+ fieldName == "zNear" || fieldName == "zFar") {
+ this._projMatrix = null; // trigger refresh
+ this.resetView();
+ }
+ else if (fieldName.indexOf("bind") >= 0) {
+ this.bind(this._vf.bind);
+ }
+ },
+
+ getCenterOfRotation: function() {
+ return this._vf.centerOfRotation;
+ },
+
+ getViewMatrix: function() {
+ return this._viewMatrix;
+ },
+
+ resetView: function() {
+ var offset = x3dom.fields.SFMatrix4f.translation(new x3dom.fields.SFVec3f(
+ (this._vf.fieldOfView[0] + this._vf.fieldOfView[2]) / 2,
+ (this._vf.fieldOfView[1] + this._vf.fieldOfView[3]) / 2, 0));
+
+ this._viewMatrix = x3dom.fields.SFMatrix4f.translation(this._vf.position).
+ mult(this._vf.orientation.toMatrix());
+ this._viewMatrix = this._viewMatrix.mult(offset).inverse();
+ },
+
+ getNear: function() {
+ return this._vf.zNear;
+ },
+
+ getFar: function() {
+ return this._vf.zFar;
+ },
+
+ getProjectionMatrix: function(aspect)
+ {
+ if (this._projMatrix == null || this._lastAspect != aspect)
+ {
+ var near = this.getNear();
+ var far = this.getFar();
+
+ var left = this._vf.fieldOfView[0];
+ var bottom = this._vf.fieldOfView[1];
+ var right = this._vf.fieldOfView[2];
+ var top = this._vf.fieldOfView[3];
+
+ this._projMatrix = x3dom.fields.SFMatrix4f.ortho(left, right, bottom, top, near, far, aspect);
+ }
+ this._lastAspect = aspect;
+
+ return this._projMatrix;
+ }
+ }
+ )
+);
+/** @namespace x3dom.nodeTypes */
+/*
+ * X3DOM JavaScript Library
+ * http://www.x3dom.org
+ *
+ * (C)2009 Fraunhofer IGD, Darmstadt, Germany
+ * Dual licensed under the MIT and GPL
+ */
+
+/* ### Viewfrustum ### */
+x3dom.registerNodeType(
+ "Viewfrustum",
+ "Navigation",
+ defineClass(x3dom.nodeTypes.X3DViewpointNode,
+
+ /**
+ * Constructor for Viewfrustum
+ * @constructs x3dom.nodeTypes.Viewfrustum
+ * @x3d x.x
+ * @component Navigation
+ * @extends x3dom.nodeTypes.X3DViewpointNode
+ * @param {Object} [ctx=null] - context object, containing initial settings like namespace
+ * @classdesc The Viewfrustum node allows to define a camera position and projection utilizing a standard OpenGL projection/modelview pair.
+ */
+ function (ctx) {
+ x3dom.nodeTypes.Viewfrustum.superClass.call(this, ctx);
+
+
+ /**
+ * Camera modelview matrix
+ * @var {x3dom.fields.SFMatrix4f} modelview
+ * @memberof x3dom.nodeTypes.Viewfrustum
+ * @initvalue 1,0,0,0
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFMatrix4f(ctx, 'modelview',
+ 1, 0, 0, 0,
+ 0, 1, 0, 0,
+ 0, 0, 1, 0,
+ 0, 0, 0, 1);
+
+ /**
+ * Camera projection matrix
+ * @var {x3dom.fields.SFMatrix4f} projection
+ * @memberof x3dom.nodeTypes.Viewfrustum
+ * @initvalue 1,0,0,0
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFMatrix4f(ctx, 'projection',
+ 1, 0, 0, 0,
+ 0, 1, 0, 0,
+ 0, 0, 1, 0,
+ 0, 0, 0, 1);
+
+ this._viewMatrix = this._vf.modelview.transpose().inverse();
+ this._projMatrix = this._vf.projection.transpose();
+
+ this._centerOfRotation = new x3dom.fields.SFVec3f(0, 0, 0);
+ // FIXME; derive near/far from current matrix, if requested!
+
+ },
+ {
+ fieldChanged: function (fieldName) {
+ if (fieldName == "modelview") {
+ this.resetView();
+ }
+ else if (fieldName == "projection") {
+ this._projMatrix = this._vf.projection.transpose();
+ }
+ else if (fieldName.indexOf("bind") >= 0) {
+ this.bind(this._vf.bind);
+ }
+ },
+
+ getCenterOfRotation: function() {
+ return this._centerOfRotation; // this field is only a little helper for examine mode
+ },
+
+ setCenterOfRotation: function(cor) {
+ this._centerOfRotation.setValues(cor); // update internal helper field
+ },
+
+ getViewMatrix: function() {
+ return this._viewMatrix;
+ },
+
+ getFieldOfView: function() {
+ return (2.0 * Math.atan(1.0 / this._projMatrix._11));
+ },
+
+ getImgPlaneHeightAtDistOne: function() {
+ return 2.0 / this._projMatrix._11;
+ },
+
+ resetView: function() {
+ this._viewMatrix = this._vf.modelview.transpose().inverse();
+ this._centerOfRotation = new x3dom.fields.SFVec3f(0, 0, 0); // reset helper, too
+ },
+
+ getProjectionMatrix: function(aspect) {
+ return this._projMatrix;
+ }
+ }
+ )
+);
+/** @namespace x3dom.nodeTypes */
+/*
+ * X3DOM JavaScript Library
+ * http://www.x3dom.org
+ *
+ * (C)2009 Fraunhofer IGD, Darmstadt, Germany
+ * Dual licensed under the MIT and GPL
+ */
+
+/* ### X3DNavigationInfoNode ### */
+x3dom.registerNodeType(
+ "X3DNavigationInfoNode",
+ "Navigation",
+ defineClass(x3dom.nodeTypes.X3DBindableNode,
+
+ /**
+ * Constructor for X3DNavigationInfoNode
+ * @constructs x3dom.nodeTypes.X3DNavigationInfoNode
+ * @x3d x.x
+ * @component Navigation
+ * @extends x3dom.nodeTypes.X3DBindableNode
+ * @param {Object} [ctx=null] - context object, containing initial settings like namespace
+ */
+ function (ctx) {
+ x3dom.nodeTypes.X3DNavigationInfoNode.superClass.call(this, ctx);
+
+ }
+ )
+);
+/** @namespace x3dom.nodeTypes */
+/*
+ * X3DOM JavaScript Library
+ * http://www.x3dom.org
+ *
+ * (C)2009 Fraunhofer IGD, Darmstadt, Germany
+ * Dual licensed under the MIT and GPL
+ */
+
+/* ### NavigationInfo ### */
+x3dom.registerNodeType(
+ "NavigationInfo",
+ "Navigation",
+ defineClass(x3dom.nodeTypes.X3DNavigationInfoNode,
+
+ /**
+ * Constructor for NavigationInfo
+ * @constructs x3dom.nodeTypes.NavigationInfo
+ * @x3d 3.3
+ * @component Navigation
+ * @status experimental
+ * @extends x3dom.nodeTypes.X3DNavigationInfoNode
+ * @param {Object} [ctx=null] - context object, containing initial settings like namespace
+ * @classdesc NavigationInfo describes the viewing model and physical characteristics of the viewer's avatar.
+ * Hint: for inspection of simple objects, usability often improves with type='EXAMINE' 'ANY' Hint: NavigationInfo types ''WALK' 'FLY'' support camera-to-object collision detection.
+ * Background, Fog, NavigationInfo, TextureBackground and Viewpoint are bindable nodes.
+ */
+ function (ctx) {
+ x3dom.nodeTypes.NavigationInfo.superClass.call(this, ctx);
+
+
+ /**
+ * Enable/disable directional light that always points in the direction the user is looking.
+ * @var {x3dom.fields.SFBool} headlight
+ * @memberof x3dom.nodeTypes.NavigationInfo
+ * @initvalue true
+ * @field x3d
+ * @instance
+ */
+ this.addField_SFBool(ctx, 'headlight', true);
+
+ /**
+ * defines the navigation type
+ * @var {x3dom.fields.MFString} type
+ * @range {"ANY","WALK","EXAMINE","FLY","LOOKAT","NONE","EXPLORE",...}
+ * @memberof x3dom.nodeTypes.NavigationInfo
+ * @initvalue ["EXAMINE","ANY"]
+ * @field x3d
+ * @instance
+ */
+ this.addField_MFString(ctx, 'type', ["EXAMINE","ANY"]);
+
+ /**
+ * Specifies the view angle and height for helicopter mode and min/max rotation angle for turntable in ]0, PI[, starting from +y (0) down to -y (PI)
+ * @var {x3dom.fields.MFFloat} typeParams
+ * @memberof x3dom.nodeTypes.NavigationInfo
+ * @initvalue [-0.4,60,0.05,2.8]
+ * @field x3dom
+ * @instance
+ */
+ this.addField_MFFloat(ctx, 'typeParams', [-0.4, 60, 0.05, 2.8]);
+
+ /**
+ * allows restricting examine and turntable navigation, overrides mouse buttons (useful for special viewers)
+ * @range [all, pan, zoom, rotate, none]
+ * @var {x3dom.fields.SFString} explorationMode
+ * @memberof x3dom.nodeTypes.NavigationInfo
+ * @initvalue 'all'
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFString(ctx, 'explorationMode', 'all');
+ // TODO; use avatarSize + visibilityLimit for projection matrix (near/far)
+
+ /**
+ * avatarSize triplet values are:
+ * (a) collision distance between user and geometry (near culling plane of the view frustrum)
+ * (b) viewer height above terrain
+ * (c) tallest height viewer can WALK over.
+ * Hint: keep (avatarSize.CollisionDistance / visibilityLimit) less then; 10,000 to avoid aliasing artifacts (i.e. polygon 'tearing').
+ * Interchange profile hint: this field may be ignored.
+ * @var {x3dom.fields.MFFloat} avatarSize
+ * @memberof x3dom.nodeTypes.NavigationInfo
+ * @initvalue [0.25,1.6,0.75]
+ * @field x3d
+ * @instance
+ */
+ this.addField_MFFloat(ctx, 'avatarSize', [0.25, 1.6, 0.75]);
+
+ /**
+ * Geometry beyond the visibilityLimit may not be rendered (far culling plane of the view frustrum).
+ * visibilityLimit=0.0 indicates an infinite visibility limit.
+ * Hint: keep visibilityLimit greater than zero.
+ * Hint: keep (avatarSize.CollisionDistance / visibilityLimit) less than 10,000 to avoid aliasing artifacts (i.e. polygon 'tearing').
+ * Interchange profile hint: this field may be ignored.
+ * @var {x3dom.fields.SFFloat} visibilityLimit
+ * @range [0, inf]
+ * @memberof x3dom.nodeTypes.NavigationInfo
+ * @initvalue 0.0
+ * @field x3d
+ * @instance
+ */
+ this.addField_SFFloat(ctx, 'visibilityLimit', 0.0);
+
+ /**
+ * Default rate at which viewer travels through scene, meters/second.
+ * Warning: default 1 m/s usually seems slow for ordinary navigation.
+ * Interchange profile hint: this field may be ignored.
+ * @var {x3dom.fields.SFFloat} speed
+ * @range [0, inf]
+ * @memberof x3dom.nodeTypes.NavigationInfo
+ * @initvalue 1.0
+ * @field x3d
+ * @instance
+ */
+ this.addField_SFFloat(ctx, 'speed', 1.0);
+ // for 'jumping' between viewpoints (bind transition time)
+
+ /**
+ * The transitionTime field specifies the duration of any viewpoint transition
+ * @var {x3dom.fields.SFTime} transitionTime
+ * @range [0, inf]
+ * @memberof x3dom.nodeTypes.NavigationInfo
+ * @initvalue 1.0
+ * @field x3d
+ * @instance
+ */
+ this.addField_SFTime(ctx, 'transitionTime', 1.0);
+
+ /**
+ * Specifies the transition mode.
+ * @var {x3dom.fields.MFString} transitionType
+ * @range [LINEAR, TELEPORT, ANIMATE, ...]
+ * @memberof x3dom.nodeTypes.NavigationInfo
+ * @initvalue ["LINEAR"]
+ * @field x3dom
+ * @instance
+ */
+ this.addField_MFString(ctx, 'transitionType', ["LINEAR"]);
+
+ this._validTypes = [
+ "none", "examine", "turntable",
+ "fly", "freefly", "lookat", "lookaround",
+ "walk", "game", "helicopter", "any"
+ ];
+ this._heliUpdated = false;
+
+ var type = this.checkType(this.getType());
+ x3dom.debug.logInfo("NavType: " + type);
+
+ },
+ {
+ fieldChanged: function(fieldName) {
+ if (fieldName == "typeParams") {
+ this._heliUpdated = false;
+ }
+ else if (fieldName == "type") {
+ var type = this.checkType(this.getType());
+
+ switch (type) {
+ case 'game':
+ this._nameSpace.doc._viewarea.initMouseState();
+ break;
+ case 'helicopter':
+ this._heliUpdated = false;
+ break;
+ case "turntable":
+ this._nameSpace.doc._viewarea.initMouseState();
+ this._nameSpace.doc._viewarea.initTurnTable(this);
+ break;
+ default:
+ break;
+ }
+
+ this._vf.type[0] = type;
+ x3dom.debug.logInfo("Switch to " + type + " mode.");
+ }
+ },
+
+ setType: function(type, viewarea) {
+ var navType = this.checkType(type.toLowerCase());
+ var oldType = this.checkType(this.getType());
+
+ switch (navType) {
+ case 'game':
+ if (oldType !== navType) {
+ if (viewarea)
+ viewarea.initMouseState();
+ else
+ this._nameSpace.doc._viewarea.initMouseState();
+ }
+ break;
+ case 'helicopter':
+ if (oldType !== navType) {
+ this._heliUpdated = false;
+ }
+ break;
+ case "turntable":
+ if (oldType !== navType) {
+ if (viewarea) {
+ viewarea.initMouseState();
+ viewarea.initTurnTable(this);
+ }
+ else {
+ this._nameSpace.doc._viewarea.initMouseState();
+ this._nameSpace.doc._viewarea.initTurnTable(this);
+ }
+ }
+ break;
+ default:
+ break;
+ }
+
+ this._vf.type[0] = navType;
+ x3dom.debug.logInfo("Switch to " + navType + " mode.");
+ },
+
+ getType: function() {
+ var type = this._vf.type[0].toLowerCase();
+ // FIXME; the following if's aren't nice!
+ if (type.length <= 1)
+ type = "none";
+ else if (type == "any")
+ type = "examine";
+ return type;
+ },
+
+ getTypeParams: function() {
+ var length = this._vf.typeParams.length;
+
+ var theta = (length >= 1) ? this._vf.typeParams[0] : -0.4;
+ var height = (length >= 2) ? this._vf.typeParams[1] : 60.0;
+ var minAngle = (length >= 3) ? this._vf.typeParams[2] : x3dom.fields.Eps;
+ var maxAngle = (length >= 4) ? this._vf.typeParams[3] : Math.PI - x3dom.fields.Eps;
+
+ // experimental HACK to switch between clamp to CoR (params[4]>0) and CoR translation in turntable mode
+ var params = [theta, height, minAngle, maxAngle];
+ if (length >= 5)
+ {
+ params.push(this._vf.typeParams[4]);
+ }
+ return params;
+ },
+
+ setTypeParams: function(params) {
+ for (var i=0; i<params.length; i++) {
+ this._vf.typeParams[i] = params[i];
+ }
+ },
+
+ checkType: function(type) {
+ if (this._validTypes.indexOf(type) > -1) {
+ return type;
+ }
+ else {
+ x3dom.debug.logWarning(type + " is no valid navigation type, use one of " +
+ this._validTypes.toString());
+ return "examine";
+ }
+ },
+
+ getExplorationMode: function() {
+ switch (this._vf.explorationMode.toLowerCase()) {
+ case "all": return 7;
+ case "rotate": return 1; //left btn
+ case "zoom": return 2; //right btn
+ case "pan": return 4; //middle btn
+ case "none": return 0; //type 'none'
+ default: return 7;
+ }
+ }
+ }
+ )
+);
+/** @namespace x3dom.nodeTypes */
+/*
+ * X3DOM JavaScript Library
+ * http://www.x3dom.org
+ *
+ * (C)2009 Fraunhofer IGD, Darmstadt, Germany
+ * Dual licensed under the MIT and GPL
+ */
+
+/* ### Billboard ### */
+x3dom.registerNodeType(
+ "Billboard",
+ "Navigation",
+ defineClass(x3dom.nodeTypes.X3DGroupingNode,
+
+ /**
+ * Constructor for Billboard
+ * @constructs x3dom.nodeTypes.Billboard
+ * @x3d 3.3
+ * @component Navigation
+ * @status full
+ * @extends x3dom.nodeTypes.X3DGroupingNode
+ * @param {Object} [ctx=null] - context object, containing initial settings like namespace
+ * @classdesc Billboard is a Grouping node that can contain most nodes.
+ * Content faces the user, rotating about the specified axis. Set axisOfRotation=0 0 0 to fully face the user's camera.
+ * Hint: Put Billboard as close to the geometry as possible, nested inside Transform for local coordinate system.
+ * Hint: don't put Viewpoint inside a Billboard.
+ * Hint: insert a Shape node before adding geometry or Appearance.
+ */
+ function (ctx) {
+ x3dom.nodeTypes.Billboard.superClass.call(this, ctx);
+
+ /**
+ * axisOfRotation direction is relative to local coordinate system. Hint: axis 0 0 0 always faces viewer.
+ * @var {x3dom.fields.SFVec3f} axisOfRotation
+ * @memberof x3dom.nodeTypes.Billboard
+ * @initvalue 0,1,0
+ * @field x3d
+ * @instance
+ */
+ this.addField_SFVec3f(ctx, 'axisOfRotation', 0, 1, 0);
+
+ this._eye = new x3dom.fields.SFVec3f(0, 0, 0);
+ this._eyeViewUp = new x3dom.fields.SFVec3f(0, 0, 0);
+ this._eyeLook = new x3dom.fields.SFVec3f(0, 0, 0);
+
+ },
+ {
+ collectDrawableObjects: function (transform, drawableCollection, singlePath, invalidateCache, planeMask, clipPlanes)
+ {
+ if (singlePath && (this._parentNodes.length > 1))
+ singlePath = false;
+
+ if (singlePath && (invalidateCache = invalidateCache || this.cacheInvalid()))
+ this.invalidateCache();
+
+ planeMask = drawableCollection.cull(transform, this.graphState(), singlePath, planeMask);
+ if (planeMask <= 0) {
+ return;
+ }
+
+ // no caching later on as transform changes almost every frame anyway
+ singlePath = false;
+
+ var vol = this.getVolume();
+
+ var min = x3dom.fields.SFVec3f.MAX();
+ var max = x3dom.fields.SFVec3f.MIN();
+ vol.getBounds(min, max);
+
+ var mat_view = drawableCollection.viewMatrix;
+
+ var center = new x3dom.fields.SFVec3f(0, 0, 0); // eye
+ center = mat_view.inverse().multMatrixPnt(center);
+
+ var mat_view_model = mat_view.mult(transform);
+ this._eye = transform.inverse().multMatrixPnt(center);
+ this._eyeViewUp = new x3dom.fields.SFVec3f(mat_view_model._10, mat_view_model._11, mat_view_model._12);
+ this._eyeLook = new x3dom.fields.SFVec3f(mat_view_model._20, mat_view_model._21, mat_view_model._22);
+
+ var rotMat = x3dom.fields.SFMatrix4f.identity();
+ var mid = max.add(min).multiply(0.5);
+ var billboard_to_viewer = this._eye.subtract(mid);
+
+ if(this._vf.axisOfRotation.equals(new x3dom.fields.SFVec3f(0, 0, 0), x3dom.fields.Eps)) {
+ var rot1 = x3dom.fields.Quaternion.rotateFromTo(
+ billboard_to_viewer, new x3dom.fields.SFVec3f(0, 0, 1));
+ rotMat = rot1.toMatrix().transpose();
+
+ var yAxis = rotMat.multMatrixPnt(new x3dom.fields.SFVec3f(0, 1, 0)).normalize();
+ var zAxis = rotMat.multMatrixPnt(new x3dom.fields.SFVec3f(0, 0, 1)).normalize();
+
+ if(!this._eyeViewUp.equals(new x3dom.fields.SFVec3f(0, 0, 0), x3dom.fields.Eps)) {
+ // new local z-axis aligned with camera z-axis
+ var rot2 = x3dom.fields.Quaternion.rotateFromTo(this._eyeLook, zAxis);
+ // new: local y-axis rotated by rot2
+ var rotatedyAxis = rot2.toMatrix().transpose().multMatrixVec(yAxis);
+ // new: rotated local y-axis aligned with camera y-axis
+ var rot3 = x3dom.fields.Quaternion.rotateFromTo(this._eyeViewUp, rotatedyAxis);
+
+ rotMat = rot2.toMatrix().transpose().mult(rotMat);
+ rotMat = rot3.toMatrix().transpose().mult(rotMat);
+ }
+ }
+ else {
+ var normalPlane = this._vf.axisOfRotation.cross(billboard_to_viewer).normalize();
+
+ if(this._eye.z < 0) {
+ normalPlane = normalPlane.multiply(-1);
+ }
+
+ var degreesToRotate = Math.asin(normalPlane.dot(new x3dom.fields.SFVec3f(0, 0, 1)));
+
+ if(this._eye.z < 0) {
+ degreesToRotate += Math.PI;
+ }
+
+ rotMat = x3dom.fields.SFMatrix4f.parseRotation(
+ this._vf.axisOfRotation.x + ", " + this._vf.axisOfRotation.y + ", " +
+ this._vf.axisOfRotation.z + ", " + degreesToRotate*(-1));
+ }
+
+ var childTransform = this.transformMatrix(transform.mult(rotMat));
+
+ for (var i=0, i_n=this._childNodes.length; i<i_n; i++)
+ {
+ var cnode = this._childNodes[i];
+ if (cnode) {
+ cnode.collectDrawableObjects(childTransform, drawableCollection, singlePath, invalidateCache, planeMask, clipPlanes);
+ }
+ }
+ }
+ }
+ )
+);
+/** @namespace x3dom.nodeTypes */
+/*
+ * X3DOM JavaScript Library
+ * http://www.x3dom.org
+ *
+ * (C)2009 Fraunhofer IGD, Darmstadt, Germany
+ * Dual licensed under the MIT and GPL
+ */
+
+// ### Collision ###
+x3dom.registerNodeType(
+ "Collision",
+ "Navigation",
+ defineClass(x3dom.nodeTypes.X3DGroupingNode,
+
+ /**
+ * Constructor for Collision
+ * @constructs x3dom.nodeTypes.Collision
+ * @x3d 3.3
+ * @component Navigation
+ * @status experimental
+ * @extends x3dom.nodeTypes.X3DGroupingNode
+ * @param {Object} [ctx=null] - context object, containing initial settings like namespace
+ * @classdesc Collision detects camera-to-object contact using current Viewpoint and NavigationInfo avatarSize.
+ * Collision is a Grouping node that handles collision detection for its children.
+ * Collision can contain a single proxy child node for substitute collision-detection geometry.
+ * Note: proxy geometry is not rendered.
+ * Note: PointSet, IndexedLineSet, LineSet and Text do not trigger collisions.
+ * Hint: improve performance using proxy for simpler contact-calculation geometry.
+ * Hint: NavigationInfo types ''WALK' 'FLY'' support camera-to-object collision detection.
+ * Hint: insert a Shape node before adding geometry or Appearance.
+ */
+ function (ctx) {
+ x3dom.nodeTypes.Collision.superClass.call(this, ctx);
+
+
+ /**
+ * Enables/disables collision detection for children and all descendants. Hint: former name quotecollidequote in VRML 97 specification.
+ * @var {x3dom.fields.SFBool} enabled
+ * @memberof x3dom.nodeTypes.Collision
+ * @initvalue true
+ * @field x3d
+ * @instance
+ */
+ this.addField_SFBool (ctx, "enabled", true);
+
+ /**
+ * alternate object to be checked for collision, in place of the children of this node.
+ * @var {x3dom.fields.SFNode} proxy
+ * @memberof x3dom.nodeTypes.Collision
+ * @initvalue x3dom.nodeTypes.X3DGroupingNode
+ * @field x3d
+ * @instance
+ */
+ this.addField_SFNode ("proxy", x3dom.nodeTypes.X3DGroupingNode);
+
+
+ // TODO; add Slots: collideTime, isActive
+ /**
+ * NOT YET IMPLEMENTED. The time of collision.
+ * @var {x3dom.fields.SFTime} collideTime
+ * @memberof x3dom.nodeTypes.Collision
+ * @initvalue 0
+ * @field x3d
+ * @instance
+ */
+ this.addField_SFTime (ctx, "collideTime", 0);
+
+ /**
+ * NOT YET IMPLEMENTED. The value of the isActive field indicates the current state of the Collision node.
+ * An isActive TRUE event is generated when a collision occurs. An isActive FALSE event is generated when a collision no longer occurs.
+ * @var {x3dom.fields.SFBool} isActive
+ * @memberof x3dom.nodeTypes.Collision
+ * @initvalue true
+ * @field x3d
+ * @instance
+ */
+ this.addField_SFBool (ctx, "isActive", true);
+ },
+ {
+ collectDrawableObjects: function (transform, drawableCollection, singlePath, invalidateCache, planeMask, clipPlanes)
+ {
+ if (singlePath && (this._parentNodes.length > 1))
+ singlePath = false;
+
+ if (singlePath && (invalidateCache = invalidateCache || this.cacheInvalid()))
+ this.invalidateCache();
+
+ planeMask = drawableCollection.cull(transform, this.graphState(), singlePath, planeMask);
+ if (planeMask <= 0) {
+ return;
+ }
+
+ var cnode, childTransform;
+
+ if (singlePath) {
+ if (!this._graph.globalMatrix) {
+ this._graph.globalMatrix = this.transformMatrix(transform);
+ }
+ childTransform = this._graph.globalMatrix;
+ }
+ else {
+ childTransform = this.transformMatrix(transform);
+ }
+
+ for (var i=0, n=this._childNodes.length; i<n; i++)
+ {
+ if ((cnode = this._childNodes[i]) && (cnode !== this._cf.proxy.node)) {
+ cnode.collectDrawableObjects(childTransform, drawableCollection, singlePath, invalidateCache, planeMask, clipPlanes);
+ }
+ }
+ }
+ }
+ )
+);
+/** @namespace x3dom.nodeTypes */
+/*
+ * X3DOM JavaScript Library
+ * http://www.x3dom.org
+ *
+ * (C)2009 Fraunhofer IGD, Darmstadt, Germany
+ * Dual licensed under the MIT and GPL
+ */
+
+// ### X3DLODNode ###
+x3dom.registerNodeType(
+ "X3DLODNode",
+ "Navigation",
+ defineClass(x3dom.nodeTypes.X3DGroupingNode,
+
+ /**
+ * Constructor for X3DLODNode
+ * @constructs x3dom.nodeTypes.X3DLODNode
+ * @x3d x.x
+ * @component Navigation
+ * @extends x3dom.nodeTypes.X3DGroupingNode
+ * @param {Object} [ctx=null] - context object, containing initial settings like namespace
+ */
+ function (ctx) {
+ x3dom.nodeTypes.X3DLODNode.superClass.call(this, ctx);
+
+
+ /**
+ * The forceTransitions field specifies whether browsers are allowed to disregard level distances in order to provide better performance.
+ * @var {x3dom.fields.SFBool} forceTransitions
+ * @memberof x3dom.nodeTypes.X3DLODNode
+ * @initvalue false
+ * @field x3d
+ * @instance
+ */
+ this.addField_SFBool (ctx, "forceTransitions", false);
+
+ /**
+ * The center field is a translation offset in the local coordinate system that specifies the centre of the LOD node for distance calculations.
+ * @var {x3dom.fields.SFVec3f} center
+ * @memberof x3dom.nodeTypes.X3DLODNode
+ * @initvalue 0,0,0
+ * @field x3d
+ * @instance
+ */
+ this.addField_SFVec3f(ctx, "center", 0, 0, 0);
+
+ this._eye = new x3dom.fields.SFVec3f(0, 0, 0);
+
+ },
+ {
+ collectDrawableObjects: function(transform, drawableCollection, singlePath, invalidateCache, planeMask, clipPlanes)
+ {
+ if (singlePath && (this._parentNodes.length > 1))
+ singlePath = false;
+
+ if (singlePath && (invalidateCache = invalidateCache || this.cacheInvalid()))
+ this.invalidateCache();
+
+ planeMask = drawableCollection.cull(transform, this.graphState(), singlePath, planeMask);
+ if (planeMask <= 0) {
+ return;
+ }
+
+ // at the moment, no caching here as children may change every frame
+ singlePath = false;
+
+ this.visitChildren(transform, drawableCollection, singlePath, invalidateCache, planeMask, clipPlanes);
+
+ //out.LODs.push( [transform, this] );
+ },
+
+ visitChildren: function(transform, drawableCollection, singlePath, invalidateCache, planeMask, clipPlanes) {
+ // overwritten
+ }
+ }
+ )
+);
+/** @namespace x3dom.nodeTypes */
+/*
+ * X3DOM JavaScript Library
+ * http://www.x3dom.org
+ *
+ * (C)2009 Fraunhofer IGD, Darmstadt, Germany
+ * Dual licensed under the MIT and GPL
+ */
+
+// ### LOD ###
+x3dom.registerNodeType(
+ "LOD",
+ "Navigation",
+ defineClass(x3dom.nodeTypes.X3DLODNode,
+
+ /**
+ * Constructor for LOD
+ * @constructs x3dom.nodeTypes.LOD
+ * @x3d 3.3
+ * @component Navigation
+ * @status experimental
+ * @extends x3dom.nodeTypes.X3DLODNode
+ * @param {Object} [ctx=null] - context object, containing initial settings like namespace
+ * @classdesc LOD (Level Of Detail) uses camera-to-object distance to switch among contained child levels.
+ * (Contained nodes are now called 'children' rather than 'level', for consistent naming among all GroupingNodeType nodes.)
+ * LOD range values go from near to far (as child geometry gets simpler for better performance).
+ * For n range values, you must have n+1 children levels! Only the currently selected children level is rendered, but all levels continue to send/receive events.
+ */
+ function (ctx) {
+ x3dom.nodeTypes.LOD.superClass.call(this, ctx);
+
+
+ /**
+ * Camera-to-object distance transitions for each child level, where range values go from near to far. For n range values, you must have n+1 child levels! Hint: can add an empty Group node as nonrendering final child.
+ * @var {x3dom.fields.MFFloat} range
+ * @range [0, inf]
+ * @memberof x3dom.nodeTypes.LOD
+ * @initvalue []
+ * @field x3d
+ * @instance
+ */
+ this.addField_MFFloat(ctx, "range", []);
+
+ this._lastRangePos = -1;
+
+ },
+ {
+ visitChildren: function(transform, drawableCollection, singlePath, invalidateCache, planeMask, clipPlanes)
+ {
+ var i=0, n=this._childNodes.length;
+
+ var mat_view = drawableCollection.viewMatrix;
+
+ var center = new x3dom.fields.SFVec3f(0, 0, 0); // eye
+ center = mat_view.inverse().multMatrixPnt(center);
+
+ //transform eye point to the LOD node's local coordinate system
+ this._eye = transform.inverse().multMatrixPnt(center);
+
+ var len = this._vf.center.subtract(this._eye).length();
+
+ //calculate range check for viewer distance d (with range in local coordinates)
+ //N+1 children nodes for N range values (L0, if d < R0, ... Ln-1, if d >= Rn-1)
+ while (i < this._vf.range.length && len > this._vf.range[i]) {
+ i++;
+ }
+ if (i && i >= n) {
+ i = n - 1;
+ }
+ this._lastRangePos = i;
+
+ var cnode = this._childNodes[i];
+ if (n && cnode)
+ {
+ var childTransform = this.transformMatrix(transform);
+ cnode.collectDrawableObjects(childTransform, drawableCollection, singlePath, invalidateCache, planeMask, clipPlanes);
+ }
+ },
+
+ getVolume: function()
+ {
+ var vol = this._graph.volume;
+
+ if (!this.volumeValid() && this._vf.render)
+ {
+ var child, childVol;
+
+ if (this._lastRangePos >= 0) {
+ child = this._childNodes[this._lastRangePos];
+
+ childVol = (child && child._vf.render === true) ? child.getVolume() : null;
+
+ if (childVol && childVol.isValid())
+ vol.extendBounds(childVol.min, childVol.max);
+ }
+ else { // first time we're here
+ for (var i=0, n=this._childNodes.length; i<n; i++)
+ {
+ if (!(child = this._childNodes[i]) || child._vf.render !== true)
+ continue;
+
+ childVol = child.getVolume();
+
+ if (childVol && childVol.isValid())
+ vol.extendBounds(childVol.min, childVol.max);
+ }
+ }
+ }
+
+ return vol;
+ },
+
+ nodeChanged: function() {
+ //this._needReRender = true;
+ this.invalidateVolume();
+ //this.invalidateCache();
+ },
+
+ fieldChanged: function(fieldName) {
+ //this._needReRender = true;
+ if (fieldName == "render" ||
+ fieldName == "center" ||
+ fieldName == "range") {
+ this.invalidateVolume();
+ //this.invalidateCache();
+ }
+ }
+ }
+ )
+);
+/** @namespace x3dom.nodeTypes */
+/*
+ * X3DOM JavaScript Library
+ * http://www.x3dom.org
+ *
+ * (C)2009 Fraunhofer IGD, Darmstadt, Germany
+ * Dual licensed under the MIT and GPL
+ */
+
+// ### DynamicLOD ###
+x3dom.registerNodeType(
+ "DynamicLOD",
+ "Navigation",
+ defineClass(x3dom.nodeTypes.X3DLODNode,
+
+ /**
+ * Constructor for DynamicLOD
+ * @constructs x3dom.nodeTypes.DynamicLOD
+ * @x3d x.x
+ * @component Navigation
+ * @extends x3dom.nodeTypes.X3DLODNode
+ * @param {Object} [ctx=null] - context object, containing initial settings like namespace
+ */
+ function (ctx) {
+ x3dom.nodeTypes.DynamicLOD.superClass.call(this, ctx);
+
+
+ /**
+ *
+ * @var {x3dom.fields.SFFloat} subScale
+ * @memberof x3dom.nodeTypes.DynamicLOD
+ * @initvalue 0.5
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFFloat(ctx, 'subScale', 0.5);
+
+ /**
+ *
+ * @var {x3dom.fields.SFVec2f} size
+ * @memberof x3dom.nodeTypes.DynamicLOD
+ * @initvalue 2,2
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFVec2f(ctx, 'size', 2, 2);
+
+ /**
+ *
+ * @var {x3dom.fields.SFVec2f} subdivision
+ * @memberof x3dom.nodeTypes.DynamicLOD
+ * @initvalue 1,1
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFVec2f(ctx, 'subdivision', 1, 1);
+
+ /**
+ *
+ * @var {x3dom.fields.SFNode} root
+ * @memberof x3dom.nodeTypes.DynamicLOD
+ * @initvalue x3dom.nodeTypes.X3DShapeNode
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFNode ('root', x3dom.nodeTypes.X3DShapeNode);
+
+
+ /**
+ *
+ * @var {x3dom.fields.SFString} urlHead
+ * @memberof x3dom.nodeTypes.DynamicLOD
+ * @initvalue "http://r"
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFString(ctx, 'urlHead', "http://r");
+
+ /**
+ *
+ * @var {x3dom.fields.SFString} urlCenter
+ * @memberof x3dom.nodeTypes.DynamicLOD
+ * @initvalue ".ortho.tiles.virtualearth.net/tiles/h"
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFString(ctx, 'urlCenter', ".ortho.tiles.virtualearth.net/tiles/h");
+
+ /**
+ *
+ * @var {x3dom.fields.SFString} urlTail
+ * @memberof x3dom.nodeTypes.DynamicLOD
+ * @initvalue ".png?g=-1"
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFString(ctx, 'urlTail', ".png?g=-1");
+
+ this.rootGeometry = new x3dom.nodeTypes.Plane(ctx);
+ this.level = 0;
+ this.quadrant = 4;
+ this.cell = "";
+
+ },
+ {
+ nodeChanged: function()
+ {
+ var root = this._cf.root.node;
+
+ if (root == null || root._cf.geometry.node != null)
+ return;
+
+ this.rootGeometry._vf.size.setValues(this._vf.size);
+ this.rootGeometry._vf.subdivision.setValues(this._vf.subdivision);
+ this.rootGeometry._vf.center.setValues(this._vf.center);
+ this.rootGeometry.fieldChanged("subdivision"); // trigger update
+
+ this._cf.root.node.addChild(this.rootGeometry); // add to shape
+ this.rootGeometry.nodeChanged();
+
+ this._cf.root.node.nodeChanged();
+
+ this._nameSpace.doc.needRender = true;
+ },
+
+ visitChildren: function(transform, drawableCollection, singlePath, invalidateCache, planeMask, clipPlanes)
+ {
+ var root = this._cf.root.node;
+
+ if (root == null)
+ return;
+
+ var mat_view = drawableCollection.viewMatrix;
+
+ var center = new x3dom.fields.SFVec3f(0, 0, 0); // eye
+ center = mat_view.inverse().multMatrixPnt(center);
+
+ //var mat_view_model = mat_view.mult(transform);
+ this._eye = transform.inverse().multMatrixPnt(center);
+
+ var l, len = this._vf.center.subtract(this._eye).length();
+
+ //calculate range check for viewer distance d (with range in local coordinates)
+ if (len > x3dom.fields.Eps && len * this._vf.subScale <= this._vf.size.length()) {
+ /* Quadrants per level: (TODO; make parameterizable, e.g. 0 and 1 might be swapped)
+ 0 | 1
+ -----
+ 2 | 3
+ */
+ if (this._childNodes.length <= 1) {
+ var offset = new Array(
+ new x3dom.fields.SFVec3f(-0.25*this._vf.size.x, 0.25*this._vf.size.y, 0),
+ new x3dom.fields.SFVec3f( 0.25*this._vf.size.x, 0.25*this._vf.size.y, 0),
+ new x3dom.fields.SFVec3f(-0.25*this._vf.size.x, -0.25*this._vf.size.y, 0),
+ new x3dom.fields.SFVec3f( 0.25*this._vf.size.x, -0.25*this._vf.size.y, 0)
+ );
+
+ for (l=0; l<4; l++) {
+ var node = new x3dom.nodeTypes.DynamicLOD();
+
+ node._nameSpace = this._nameSpace;
+ node._eye.setValues(this._eye);
+
+ node.level = this.level + 1;
+ node.quadrant = l;
+ node.cell = this.cell + l;
+
+ node._vf.urlHead = this._vf.urlHead;
+ node._vf.urlCenter = this._vf.urlCenter;
+ node._vf.urlTail = this._vf.urlTail;
+
+ node._vf.center = this._vf.center.add(offset[l]);
+ node._vf.size = this._vf.size.multiply(0.5);
+ node._vf.subdivision.setValues(this._vf.subdivision);
+
+ var app = new x3dom.nodeTypes.Appearance();
+
+ //var mat = new x3dom.nodeTypes.Material();
+ //mat._vf.diffuseColor = new x3dom.fields.SFVec3f(Math.random(),Math.random(),Math.random());
+ //
+ //app.addChild(mat);
+ //mat.nodeChanged();
+
+ var tex = new x3dom.nodeTypes.ImageTexture();
+ tex._nameSpace = this._nameSpace;
+ tex._vf.url[0] = this._vf.urlHead + node.quadrant + this._vf.urlCenter + node.cell + this._vf.urlTail;
+ //x3dom.debug.logInfo(tex._vf.url[0]);
+
+ app.addChild(tex);
+ tex.nodeChanged();
+
+ var shape = new x3dom.nodeTypes.Shape();
+ shape._nameSpace = this._nameSpace;
+
+ shape.addChild(app);
+ app.nodeChanged();
+
+ node.addChild(shape, "root");
+ shape.nodeChanged();
+
+ this.addChild(node);
+ node.nodeChanged();
+ }
+ }
+ else {
+ for (l=1; l<this._childNodes.length; l++) {
+ this._childNodes[l].collectDrawableObjects(transform, drawableCollection, singlePath, invalidateCache, planeMask, clipPlanes);
+ }
+ }
+ }
+ else {
+ root.collectDrawableObjects(transform, drawableCollection, singlePath, invalidateCache, planeMask, clipPlanes);
+ }
+ },
+
+ getVolume: function() {
+ var vol = this._graph.volume;
+
+ if (!vol.isValid()) {
+ vol.min.setValues(this._vf.center);
+ vol.min.x -= 0.5 * this._vf.size.x;
+ vol.min.y -= 0.5 * this._vf.size.y;
+ vol.min.z -= x3dom.fields.Eps;
+
+ vol.max.setValues(this._vf.center);
+ vol.max.x += 0.5 * this._vf.size.x;
+ vol.max.y += 0.5 * this._vf.size.y;
+ vol.max.z += x3dom.fields.Eps;
+ }
+
+ return vol;
+ }
+ }
+ )
+);
+/** @namespace x3dom.nodeTypes */
+/*
+ * X3DOM JavaScript Library
+ * http://www.x3dom.org
+ *
+ * (C)2009 Fraunhofer IGD, Darmstadt, Germany
+ * Dual licensed under the MIT and GPL
+ */
+
+/* ### X3DFontStyleNode ### */
+x3dom.registerNodeType(
+ "X3DFontStyleNode",
+ "Text",
+ defineClass(x3dom.nodeTypes.X3DNode,
+
+ /**
+ * Constructor for X3DFontStyleNode
+ * @constructs x3dom.nodeTypes.X3DFontStyleNode
+ * @x3d 3.3
+ * @component Text
+ * @status full
+ * @extends x3dom.nodeTypes.X3DNode
+ * @param {Object} [ctx=null] - context object, containing initial settings like namespace
+ * @classdesc This abstract node type is the base node type for all font style nodes.
+ */
+ function (ctx) {
+ x3dom.nodeTypes.X3DFontStyleNode.superClass.call(this, ctx);
+
+ }
+ )
+);
+/** @namespace x3dom.nodeTypes */
+/*
+ * X3DOM JavaScript Library
+ * http://www.x3dom.org
+ *
+ * (C)2009 Fraunhofer IGD, Darmstadt, Germany
+ * Dual licensed under the MIT and GPL
+ */
+
+/* ### FontStyle ### */
+x3dom.registerNodeType(
+ "FontStyle",
+ "Text",
+ defineClass(x3dom.nodeTypes.X3DFontStyleNode,
+
+ /**
+ * Constructor for FontStyle
+ * @constructs x3dom.nodeTypes.FontStyle
+ * @x3d 3.3
+ * @component Text
+ * @status full
+ * @extends x3dom.nodeTypes.X3DFontStyleNode
+ * @param {Object} [ctx=null] - context object, containing initial settings like namespace
+ * @classdesc The FontStyle node defines the size, family, and style used for Text nodes, as well as the direction of the text strings and any language-specific rendering techniques used for non-English text.
+ */
+ function (ctx) {
+ x3dom.nodeTypes.FontStyle.superClass.call(this, ctx);
+
+
+ /**
+ * Defines the text family.
+ * @var {x3dom.fields.MFString} family
+ * @memberof x3dom.nodeTypes.FontStyle
+ * @initvalue ['SERIF']
+ * @field x3d
+ * @instance
+ */
+ this.addField_MFString(ctx, 'family', ['SERIF']);
+
+ /**
+ * Specifies whether the text is shown horizontally or vertically.
+ * @var {x3dom.fields.SFBool} horizontal
+ * @memberof x3dom.nodeTypes.FontStyle
+ * @initvalue true
+ * @field x3d
+ * @instance
+ */
+ this.addField_SFBool(ctx, 'horizontal', true);
+
+ /**
+ * Specifies where the text is anchored.
+ * @var {x3dom.fields.MFString} justify
+ * @range ["BEGIN","END","FIRST","MIDDLE",""]
+ * @memberof x3dom.nodeTypes.FontStyle
+ * @initvalue ['BEGIN']
+ * @field x3d
+ * @instance
+ */
+ this.addField_MFString(ctx, 'justify', ['BEGIN']);
+
+ /**
+ * The language field specifies the context of the language for the text string in the form of a language and a country in which that language is used.
+ * @var {x3dom.fields.SFString} language
+ * @memberof x3dom.nodeTypes.FontStyle
+ * @initvalue ""
+ * @field x3d
+ * @instance
+ */
+ this.addField_SFString(ctx, 'language', "");
+
+ /**
+ * Specifies whether the text is shown from left to right or from right to left.
+ * @var {x3dom.fields.SFBool} leftToRight
+ * @memberof x3dom.nodeTypes.FontStyle
+ * @initvalue true
+ * @field x3d
+ * @instance
+ */
+ this.addField_SFBool(ctx, 'leftToRight', true);
+
+ /**
+ * Sets the size of the text.
+ * @var {x3dom.fields.SFFloat} size
+ * @range [0, inf]
+ * @memberof x3dom.nodeTypes.FontStyle
+ * @initvalue 1.0
+ * @field x3d
+ * @instance
+ */
+ this.addField_SFFloat(ctx, 'size', 1.0);
+
+ /**
+ * Sets the spacing between lines of text, relative to the text size.
+ * @var {x3dom.fields.SFFloat} spacing
+ * @range [0, inf]
+ * @memberof x3dom.nodeTypes.FontStyle
+ * @initvalue 1.0
+ * @field x3d
+ * @instance
+ */
+ this.addField_SFFloat(ctx, 'spacing', 1.0);
+
+ /**
+ * Sets the text style.
+ * @var {x3dom.fields.SFString} style
+ * @range ["PLAIN","BOLD","ITALIC","BOLDITALIC",""]
+ * @memberof x3dom.nodeTypes.FontStyle
+ * @initvalue "PLAIN"
+ * @field x3d
+ * @instance
+ */
+ this.addField_SFString(ctx, 'style', "PLAIN");
+
+ /**
+ * Specifies whether the text flows from top to bottom or from bottom to top.
+ * @var {x3dom.fields.SFBool} topToBottom
+ * @memberof x3dom.nodeTypes.FontStyle
+ * @initvalue true
+ * @field x3d
+ * @instance
+ */
+ this.addField_SFBool(ctx, 'topToBottom', true);
+
+ },
+ {
+ fieldChanged: function(fieldName) {
+ if (fieldName == 'family' || fieldName == 'horizontal' || fieldName == 'justify' ||
+ fieldName == 'language' || fieldName == 'leftToRight' || fieldName == 'size' ||
+ fieldName == 'spacing' || fieldName == 'style' || fieldName == 'topToBottom') {
+ Array.forEach(this._parentNodes, function (node) {
+ node.fieldChanged(fieldName);
+ });
+ }
+ }
+ }
+ )
+);
+
+x3dom.nodeTypes.FontStyle.defaultNode = function() {
+ if (!x3dom.nodeTypes.FontStyle._defaultNode) {
+ x3dom.nodeTypes.FontStyle._defaultNode = new x3dom.nodeTypes.FontStyle();
+ x3dom.nodeTypes.FontStyle._defaultNode.nodeChanged();
+ }
+ return x3dom.nodeTypes.FontStyle._defaultNode;
+};
+/** @namespace x3dom.nodeTypes */
+/*
+ * X3DOM JavaScript Library
+ * http://www.x3dom.org
+ *
+ * (C)2009 Fraunhofer IGD, Darmstadt, Germany
+ * Dual licensed under the MIT and GPL
+ */
+
+/* ### Text ### */
+x3dom.registerNodeType(
+ "Text",
+ "Text",
+ defineClass(x3dom.nodeTypes.X3DGeometryNode,
+
+ /**
+ * Constructor for Text
+ * @constructs x3dom.nodeTypes.Text
+ * @x3d 3.3
+ * @component Text
+ * @status experimental
+ * @extends x3dom.nodeTypes.X3DGeometryNode
+ * @param {Object} [ctx=null] - context object, containing initial settings like namespace
+ * @classdesc The Text node specifies a two-sided, flat text string object positioned in the Z=0 plane of the local coordinate system based on values defined in the fontStyle field.
+ * Text nodes may contain multiple text strings specified using the UTF-8 encoding.
+ * The text strings are stored in the order in which the text mode characters are to be produced as defined by the parameters in the FontStyle node.
+ */
+ function (ctx) {
+ x3dom.nodeTypes.Text.superClass.call(this, ctx);
+
+
+ /**
+ * The text strings are contained in the string field.
+ * @var {x3dom.fields.MFString} string
+ * @memberof x3dom.nodeTypes.Text
+ * @initvalue []
+ * @field x3d
+ * @instance
+ */
+ this.addField_MFString(ctx, 'string', []);
+
+ /**
+ * The length field contains an MFFloat value that specifies the length of each text string in the local coordinate system.
+ * The length of each line of type is measured horizontally for horizontal text (FontStyle node: horizontal=TRUE) and vertically for vertical text (FontStyle node: horizontal=FALSE).
+ * @var {x3dom.fields.MFFloat} length
+ * @range [0, inf]
+ * @memberof x3dom.nodeTypes.Text
+ * @initvalue []
+ * @field x3d
+ * @instance
+ */
+ this.addField_MFFloat(ctx, 'length', []);
+
+ /**
+ * The maxExtent field limits and compresses all of the text strings if the length of the maximum string is longer than the maximum extent, as measured in the local coordinate system. If the text string with the maximum length is shorter than the maxExtent, then there is no compressing.
+ * The maximum extent is measured horizontally for horizontal text (FontStyle node: horizontal=TRUE) and vertically for vertical text (FontStyle node: horizontal=FALSE).
+ * @var {x3dom.fields.SFFloat} maxExtent
+ * @range [0, inf]
+ * @memberof x3dom.nodeTypes.Text
+ * @initvalue 0.0
+ * @field x3d
+ * @instance
+ */
+ this.addField_SFFloat(ctx, 'maxExtent', 0.0);
+
+ /**
+ * The fontStyle field contains one FontStyle node that specifies the font size, font family and style, direction of the text strings, and any specific language rendering techniques used for the text.
+ * @var {x3dom.fields.SFNode} fontStyle
+ * @memberof x3dom.nodeTypes.Text
+ * @initvalue x3dom.nodeTypes.X3DFontStyleNode
+ * @field x3d
+ * @instance
+ */
+ this.addField_SFNode ('fontStyle', x3dom.nodeTypes.X3DFontStyleNode);
+
+ this._mesh._positions[0] = [];
+ this._mesh._normals[0] = [0,0,1, 0,0,1, 0,0,1, 0,0,1];
+ this._mesh._texCoords[0] = [0,0, 1,0, 1,1, 0,1];
+ this._mesh._colors[0] = [];
+ this._mesh._indices[0] = [0,1,2, 2,3,0];
+ this._mesh._invalidate = true;
+ this._mesh._numFaces = 2;
+ this._mesh._numCoords = 4;
+
+ },
+ {
+ nodeChanged: function() {
+ if (!this._cf.fontStyle.node) {
+ this.addChild(x3dom.nodeTypes.FontStyle.defaultNode());
+ }
+ this.invalidateVolume();
+ },
+
+ fieldChanged: function(fieldName) {
+ if (fieldName == 'string' || fieldName == 'length' || fieldName == 'maxExtent') {
+ this.invalidateVolume();
+ Array.forEach(this._parentNodes, function (node) {
+ node.setAllDirty();
+ });
+ }
+ }
+ }
+ ) // defineClass
+); // registerNodeType
+/** @namespace x3dom.nodeTypes */
+/*
+ * X3DOM JavaScript Library
+ * http://www.x3dom.org
+ *
+ * (C)2009 Fraunhofer IGD, Darmstadt, Germany
+ * Dual licensed under the MIT and GPL
+ */
+
+/* ### X3DSoundNode ### */
+x3dom.registerNodeType(
+ "X3DSoundNode",
+ "Sound",
+ defineClass(x3dom.nodeTypes.X3DChildNode,
+
+ /**
+ * Constructor for X3DSoundNode
+ * @constructs x3dom.nodeTypes.X3DSoundNode
+ * @x3d 3.3
+ * @component Sound
+ * @status full
+ * @extends x3dom.nodeTypes.X3DChildNode
+ * @param {Object} [ctx=null] - context object, containing initial settings like namespace
+ * @CLASSDESC This abstract node type is the base for all sound nodes.
+ */
+ function (ctx) {
+ x3dom.nodeTypes.X3DSoundNode.superClass.call(this, ctx);
+
+ }
+ )
+);
+/** @namespace x3dom.nodeTypes */
+/*
+ * X3DOM JavaScript Library
+ * http://www.x3dom.org
+ *
+ * (C)2009 Fraunhofer IGD, Darmstadt, Germany
+ * Dual licensed under the MIT and GPL
+ */
+
+/* ### Sound ### */
+x3dom.registerNodeType(
+ "Sound",
+ "Sound",
+ defineClass(x3dom.nodeTypes.X3DSoundNode,
+
+ /**
+ * Constructor for Sound
+ * @constructs x3dom.nodeTypes.Sound
+ * @x3d 3.3
+ * @component Sound
+ * @status experimental
+ * @extends x3dom.nodeTypes.X3DSoundNode
+ * @param {Object} [ctx=null] - context object, containing initial settings like namespace
+ * @classdesc The Sound node specifies the spatial presentation of a sound in a X3D scene.
+ */
+ function (ctx) {
+ x3dom.nodeTypes.Sound.superClass.call(this, ctx);
+
+
+ /**
+ * The source field specifies the sound source for the Sound node. If the source field is not specified, the Sound node will not emit audio.
+ * The source field shall specify either an AudioClip node or a MovieTexture node.
+ * If a MovieTexture node is specified as the sound source, the MovieTexture shall refer to a movie format that supports sound.
+ * @var {x3dom.fields.SFNode} source
+ * @memberof x3dom.nodeTypes.Sound
+ * @initvalue x3dom.nodeTypes.X3DSoundSourceNode
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFNode('source', x3dom.nodeTypes.X3DSoundSourceNode);
+
+ },
+ {
+ nodeChanged: function()
+ {
+ if (this._cf.source.node || !this._xmlNode) {
+ return;
+ }
+
+ x3dom.debug.logInfo("No AudioClip child node given, searching for &lt;audio&gt; elements...");
+ /** USAGE e.g.:
+ <sound>
+ <audio src='sound/spita.wav' loop='loop'></audio>
+ </sound>
+ */
+ try {
+ Array.forEach( this._xmlNode.childNodes, function (childDomNode) {
+ if (childDomNode.nodeType === 1)
+ {
+ // For testing: look for <audio> element if no child
+ x3dom.debug.logInfo("### Found &lt;"+childDomNode.nodeName+"&gt; tag.");
+
+ if (childDomNode.localName.toLowerCase() === "audio")
+ {
+ var loop = childDomNode.getAttribute("loop");
+ loop = loop ? (loop.toLowerCase() === "loop") : false;
+
+ // TODO; check if crash still exists and clean-up code
+ // work around strange crash in Chrome
+ // by creating new audio element here
+
+ /*
+ var src = childDomNode.getAttribute("src");
+ var newNode = document.createElement('audio');
+ newNode.setAttribute('autobuffer', 'true');
+ newNode.setAttribute('src', src);
+ */
+ var newNode = childDomNode.cloneNode(false);
+
+ childDomNode.parentNode.removeChild(childDomNode);
+ childDomNode = null;
+
+ if(navigator.appName != "Microsoft Internet Explorer") {
+ document.body.appendChild(newNode);
+ }
+
+ var startAudio = function() {
+ newNode.play();
+ };
+
+ var audioDone = function() {
+ if (loop) {
+ newNode.play();
+ }
+ };
+
+ newNode.addEventListener("canplaythrough", startAudio, true);
+ newNode.addEventListener("ended", audioDone, true);
+ }
+ }
+ } );
+ }
+ catch(e) {
+ x3dom.debug.logException(e);
+ }
+ }
+ }
+ )
+);
+/** @namespace x3dom.nodeTypes */
+/*
+ * X3DOM JavaScript Library
+ * http://www.x3dom.org
+ *
+ * (C)2009 Fraunhofer IGD, Darmstadt, Germany
+ * Dual licensed under the MIT and GPL
+ */
+
+/* ### X3DSoundSourceNode ### */
+x3dom.registerNodeType(
+ "X3DSoundSourceNode",
+ "Sound",
+ defineClass(x3dom.nodeTypes.X3DTimeDependentNode,
+
+ /**
+ * Constructor for X3DSoundSourceNode
+ * @constructs x3dom.nodeTypes.X3DSoundSourceNode
+ * @x3d 3.3
+ * @component Sound
+ * @status experimental
+ * @extends x3dom.nodeTypes.X3DTimeDependentNode
+ * @param {Object} [ctx=null] - context object, containing initial settings like namespace
+ * @classdesc This abstract node type is used to derive node types that can emit audio data.
+ */
+ function (ctx) {
+ x3dom.nodeTypes.X3DSoundSourceNode.superClass.call(this, ctx);
+
+ }
+ )
+);
+/** @namespace x3dom.nodeTypes */
+/*
+ * X3DOM JavaScript Library
+ * http://www.x3dom.org
+ *
+ * (C)2009 Fraunhofer IGD, Darmstadt, Germany
+ * Dual licensed under the MIT and GPL
+ */
+
+/* ### AudioClip ### */
+x3dom.registerNodeType(
+ "AudioClip",
+ "Sound",
+ defineClass(x3dom.nodeTypes.X3DSoundSourceNode,
+
+ /**
+ * Constructor for AudioClip
+ * @constructs x3dom.nodeTypes.AudioClip
+ * @x3d 3.3
+ * @component Sound
+ * @status experimental
+ * @extends x3dom.nodeTypes.X3DSoundSourceNode
+ * @param {Object} [ctx=null] - context object, containing initial settings like namespace
+ * @classdesc An AudioClip node specifies audio data that can be referenced by Sound nodes.
+ */
+ function (ctx) {
+ x3dom.nodeTypes.AudioClip.superClass.call(this, ctx);
+
+
+ /**
+ * The url field specifies the URL from which the sound is loaded.
+ * @var {x3dom.fields.MFString} url
+ * @memberof x3dom.nodeTypes.AudioClip
+ * @initvalue []
+ * @field x3dom
+ * @instance
+ */
+ this.addField_MFString(ctx, 'url', []);
+
+ /**
+ * Specifies whether the clip is enabled.
+ * @var {x3dom.fields.SFBool} enabled
+ * @memberof x3dom.nodeTypes.AudioClip
+ * @initvalue true
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFBool(ctx, 'enabled', false);
+
+ /**
+ * Specifies whether the clip loops when finished.
+ * @var {x3dom.fields.SFBool} loop
+ * @memberof x3dom.nodeTypes.AudioClip
+ * @initvalue false
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFBool(ctx, 'loop', false);
+
+ this._audio = document.createElement('audio');
+ //this._audio.setAttribute('preload', 'none');
+ //this._audio.setAttribute('autoplay', 'true');
+ if(navigator.appName != "Microsoft Internet Explorer") {
+ document.body.appendChild(this._audio);
+ }
+
+ this._sources = [];
+
+ },
+ {
+ nodeChanged: function()
+ {
+ this._createSources = function()
+ {
+ this._sources = [];
+ for (var i=0; i<this._vf.url.length; i++)
+ {
+ var audioUrl = this._nameSpace.getURL(this._vf.url[i]);
+ x3dom.debug.logInfo('Adding sound file: ' + audioUrl);
+ var src = document.createElement('source');
+ src.setAttribute('src', audioUrl);
+ this._sources.push(src);
+ this._audio.appendChild(src);
+ }
+ };
+
+ var that = this;
+
+ this._startAudio = function()
+ {
+ that._audio.loop = that._vf.loop ? "loop" : "";
+ if(that._vf.enabled === true)
+ {
+ that._audio.play();
+ }
+ };
+
+ this._stopAudio = function()
+ {
+ that._audio.pause();
+ };
+
+ this._audioEnded = function()
+ {
+ if (that._vf.enabled === true && that._vf.loop === true)
+ {
+ that._startAudio();
+ }
+ };
+
+ var log = function(e)
+ {
+ x3dom.debug.logWarning("MediaEvent error:"+e);
+ };
+
+ this._audio.addEventListener("canplaythrough", this._startAudio, true);
+ this._audio.addEventListener("ended", this._audioEnded, true);
+ this._audio.addEventListener("error", log, true);
+ this._audio.addEventListener("pause", this._audioEnded, true);
+
+ this._createSources();
+ },
+
+ fieldChanged: function(fieldName)
+ {
+ if (fieldName === "enabled")
+ {
+ if (this._vf.enabled === true)
+ {
+ this._startAudio();
+ }
+ else
+ {
+ this._stopAudio();
+ }
+ }
+ else if (fieldName === "loop")
+ {
+ //this._audio.loop = this._vf.loop;
+ }
+ else if (fieldName === "url")
+ {
+ this._stopAudio();
+ while (this._audio.hasChildNodes())
+ {
+ this._audio.removeChild(this._audio.firstChild);
+ }
+
+ for (var i=0; i<this._vf.url.length; i++)
+ {
+ var audioUrl = this._nameSpace.getURL(this._vf.url[i]);
+ x3dom.debug.logInfo('Adding sound file: ' + audioUrl);
+ var src = document.createElement('source');
+ src.setAttribute('src', audioUrl);
+ this._audio.appendChild(src);
+ }
+ }
+ },
+
+ shutdown: function() {
+ if (this._audio) {
+ this._audio.pause();
+ while (this._audio.hasChildNodes()) {
+ this._audio.removeChild(this._audio.firstChild);
+ }
+ document.body.removeChild(this._audio);
+ this._audio = null;
+ }
+ }
+ }
+ )
+);
+/** @namespace x3dom.nodeTypes */
+/*
+ * X3DOM JavaScript Library
+ * http://www.x3dom.org
+ *
+ * (C)2009 Fraunhofer IGD, Darmstadt, Germany
+ * Dual licensed under the MIT and GPL
+ */
+
+/* ### X3DTextureTransformNode ### */
+x3dom.registerNodeType(
+ "X3DTextureTransformNode",
+ "Texturing",
+ defineClass(x3dom.nodeTypes.X3DAppearanceChildNode,
+
+ /**
+ * Constructor for X3DTextureTransformNode
+ * @constructs x3dom.nodeTypes.X3DTextureTransformNode
+ * @x3d 3.3
+ * @component Texturing
+ * @status full
+ * @extends x3dom.nodeTypes.X3DAppearanceChildNode
+ * @param {Object} [ctx=null] - context object, containing initial settings like namespace
+ * @classdesc This abstract node type is the base type for all node types which specify a transformation of texture coordinates.
+ */
+ function (ctx) {
+ x3dom.nodeTypes.X3DTextureTransformNode.superClass.call(this, ctx);
+
+ }
+ )
+);
+/** @namespace x3dom.nodeTypes */
+/*
+ * X3DOM JavaScript Library
+ * http://www.x3dom.org
+ *
+ * (C)2009 Fraunhofer IGD, Darmstadt, Germany
+ * Dual licensed under the MIT and GPL
+ */
+
+/* ### TextureTransform ### */
+x3dom.registerNodeType(
+ "TextureTransform",
+ "Texturing",
+ defineClass(x3dom.nodeTypes.X3DTextureTransformNode,
+
+ /**
+ * Constructor for TextureTransform
+ * @constructs x3dom.nodeTypes.TextureTransform
+ * @x3d 3.3
+ * @component Texturing
+ * @status full
+ * @extends x3dom.nodeTypes.X3DTextureTransformNode
+ * @param {Object} [ctx=null] - context object, containing initial settings like namespace
+ * @classdesc The TextureTransform node defines a 2D transformation that is applied to texture coordinates. This node affects the way textures coordinates are applied to the geometric surface. The transformation consists of (in order):
+ * a translation; a rotation about the centre point; a non-uniform scale about the centre point.
+ */
+ function (ctx) {
+ x3dom.nodeTypes.TextureTransform.superClass.call(this, ctx);
+
+
+ /**
+ * The center field specifies a translation offset in texture coordinate space about which the rotation and scale fields are applied.
+ * @var {x3dom.fields.SFVec2f} center
+ * @memberof x3dom.nodeTypes.TextureTransform
+ * @initvalue 0,0
+ * @field x3d
+ * @instance
+ */
+ this.addField_SFVec2f(ctx, 'center', 0, 0);
+
+ /**
+ * The rotation field specifies a rotation in angle base units of the texture coordinates about the center point after the scale has been applied.
+ * A positive rotation value makes the texture coordinates rotate counterclockwise about the centre, thereby rotating the appearance of the texture itself clockwise.
+ * @var {x3dom.fields.SFFloat} rotation
+ * @memberof x3dom.nodeTypes.TextureTransform
+ * @initvalue 0
+ * @field x3d
+ * @instance
+ */
+ this.addField_SFFloat(ctx, 'rotation', 0);
+
+ /**
+ * The scale field specifies a scaling factor in S and T of the texture coordinates about the center point.
+ * @var {x3dom.fields.SFVec2f} scale
+ * @memberof x3dom.nodeTypes.TextureTransform
+ * @initvalue 1,1
+ * @field x3d
+ * @instance
+ */
+ this.addField_SFVec2f(ctx, 'scale', 1, 1);
+
+ /**
+ * The translation field specifies a translation of the texture coordinates.
+ * @var {x3dom.fields.SFVec2f} translation
+ * @memberof x3dom.nodeTypes.TextureTransform
+ * @initvalue 0,0
+ * @field x3d
+ * @instance
+ */
+ this.addField_SFVec2f(ctx, 'translation', 0, 0);
+
+ //Tc' = -C * S * R * C * T * Tc
+ var negCenter = new x3dom.fields.SFVec3f(-this._vf.center.x, -this._vf.center.y, 1);
+ var posCenter = new x3dom.fields.SFVec3f(this._vf.center.x, this._vf.center.y, 0);
+ var trans3 = new x3dom.fields.SFVec3f(this._vf.translation.x, this._vf.translation.y, 0);
+ var scale3 = new x3dom.fields.SFVec3f(this._vf.scale.x, this._vf.scale.y, 0);
+
+ this._trafo = x3dom.fields.SFMatrix4f.translation(negCenter).
+ mult(x3dom.fields.SFMatrix4f.scale(scale3)).
+ mult(x3dom.fields.SFMatrix4f.rotationZ(this._vf.rotation)).
+ mult(x3dom.fields.SFMatrix4f.translation(posCenter.add(trans3)));
+
+ },
+ {
+ fieldChanged: function (fieldName) {
+ //Tc' = -C * S * R * C * T * Tc
+ if (fieldName == 'center' || fieldName == 'rotation' ||
+ fieldName == 'scale' || fieldName == 'translation') {
+
+ var negCenter = new x3dom.fields.SFVec3f(-this._vf.center.x, -this._vf.center.y, 1);
+ var posCenter = new x3dom.fields.SFVec3f(this._vf.center.x, this._vf.center.y, 0);
+ var trans3 = new x3dom.fields.SFVec3f(this._vf.translation.x, this._vf.translation.y, 0);
+ var scale3 = new x3dom.fields.SFVec3f(this._vf.scale.x, this._vf.scale.y, 0);
+
+ this._trafo = x3dom.fields.SFMatrix4f.translation(negCenter).
+ mult(x3dom.fields.SFMatrix4f.scale(scale3)).
+ mult(x3dom.fields.SFMatrix4f.rotationZ(this._vf.rotation)).
+ mult(x3dom.fields.SFMatrix4f.translation(posCenter.add(trans3)));
+ }
+ },
+
+ texTransformMatrix: function() {
+ return this._trafo;
+ }
+ }
+ )
+);
+/** @namespace x3dom.nodeTypes */
+/*
+ * X3DOM JavaScript Library
+ * http://www.x3dom.org
+ *
+ * (C)2009 Fraunhofer IGD, Darmstadt, Germany
+ * Dual licensed under the MIT and GPL
+ */
+
+/* ### TextureProperties ### */
+x3dom.registerNodeType(
+ "TextureProperties",
+ "Texturing",
+ defineClass(x3dom.nodeTypes.X3DNode,
+
+ /**
+ * Constructor for TextureProperties
+ * @constructs x3dom.nodeTypes.TextureProperties
+ * @x3d 3.3
+ * @component Texturing
+ * @status full
+ * @extends x3dom.nodeTypes.X3DNode
+ * @param {Object} [ctx=null] - context object, containing initial settings like namespace
+ * @classdesc TextureProperties allows fine control over a texture's application.
+ * This node can be used to set the texture properties for a node with a textureProperties field.
+ * A texture with a TextureProperties node will ignore the repeatS and repeatT fields on the texture.
+ */
+ function (ctx) {
+ x3dom.nodeTypes.TextureProperties.superClass.call(this, ctx);
+
+
+ /**
+ * The anisotropicDegree field describes the minimum degree of anisotropy to account for in texture filtering.
+ * A value of 1 implies no anisotropic filtering.
+ * Values above the system's maximum supported value will be clamped to the maximum allowed.
+ * @var {x3dom.fields.SFFloat} anisotropicDegree
+ * @memberof x3dom.nodeTypes.TextureProperties
+ * @initvalue 1.0
+ * @field x3d
+ * @instance
+ */
+ this.addField_SFFloat(ctx, 'anisotropicDegree', 1.0);
+
+ /**
+ * The borderColor field describes the color to use for border pixels.
+ * @var {x3dom.fields.SFColorRGBA} borderColor
+ * @memberof x3dom.nodeTypes.TextureProperties
+ * @initvalue 0,0,0,0
+ * @field x3d
+ * @instance
+ */
+ this.addField_SFColorRGBA(ctx, 'borderColor', 0, 0, 0, 0);
+
+ /**
+ * The borderWidth field describes the number of pixels to use for a texture border.
+ * @var {x3dom.fields.SFInt32} borderWidth
+ * @memberof x3dom.nodeTypes.TextureProperties
+ * @initvalue 0
+ * @field x3d
+ * @instance
+ */
+ this.addField_SFInt32(ctx, 'borderWidth', 0);
+
+ /**
+ * The boundaryModeS field describes the way S texture coordinate boundaries are handled.
+ * @var {x3dom.fields.SFString} boundaryModeS
+ * @memberof x3dom.nodeTypes.TextureProperties
+ * @initvalue "REPEAT"
+ * @field x3d
+ * @instance
+ */
+ this.addField_SFString(ctx, 'boundaryModeS', "REPEAT");
+
+ /**
+ * The boundaryModeS field describes the way T texture coordinate boundaries are handled.
+ * @var {x3dom.fields.SFString} boundaryModeT
+ * @memberof x3dom.nodeTypes.TextureProperties
+ * @initvalue "REPEAT"
+ * @field x3d
+ * @instance
+ */
+ this.addField_SFString(ctx, 'boundaryModeT', "REPEAT");
+
+ /**
+ * The boundaryModeS field describes the way R texture coordinate boundaries are handled.
+ * This field only applies to three dimensional textures and shall be ignored by other texture types.
+ * @var {x3dom.fields.SFString} boundaryModeR
+ * @memberof x3dom.nodeTypes.TextureProperties
+ * @initvalue "REPEAT"
+ * @field x3d
+ * @instance
+ */
+ this.addField_SFString(ctx, 'boundaryModeR', "REPEAT");
+
+ /**
+ * The magnificationFilter field describes the way textures are filtered when the image is smaller than the screen space representation.
+ * @var {x3dom.fields.SFString} magnificationFilter
+ * @memberof x3dom.nodeTypes.TextureProperties
+ * @initvalue "FASTEST"
+ * @field x3d
+ * @instance
+ */
+ this.addField_SFString(ctx, 'magnificationFilter', "FASTEST");
+
+ /**
+ * The minificationFilter field describes the way textures are filtered when the image is larger than the screen space representation.
+ * Modes with MIPMAP in the name require mipmaps. If mipmaps are not provided, the mode shall pick the corresponding non-mipmapped mode
+ * @var {x3dom.fields.SFString} minificationFilter
+ * @memberof x3dom.nodeTypes.TextureProperties
+ * @initvalue "FASTEST"
+ * @field x3d
+ * @instance
+ */
+ this.addField_SFString(ctx, 'minificationFilter', "FASTEST");
+
+ /**
+ * The textureCompression field specifies the preferred image compression method to be used during rendering.
+ * @var {x3dom.fields.SFString} textureCompression
+ * @memberof x3dom.nodeTypes.TextureProperties
+ * @initvalue "FASTEST"
+ * @field x3d
+ * @instance
+ */
+ this.addField_SFString(ctx, 'textureCompression', "FASTEST");
+
+ /**
+ * The texturePriority field describes the texture residence priority for allocating texture memory. Zero indicates the lowest priority and 1 indicates the highest priority.
+ * @var {x3dom.fields.SFFloat} texturePriority
+ * @memberof x3dom.nodeTypes.TextureProperties
+ * @initvalue 0
+ * @field x3d
+ * @instance
+ */
+ this.addField_SFFloat(ctx, 'texturePriority', 0);
+
+ /**
+ * The generateMipMaps field describes whether mipmaps should be generated for the texture. Mipmaps are required for filtering modes with MIPMAP in their value.
+ * @var {x3dom.fields.SFBool} generateMipMaps
+ * @memberof x3dom.nodeTypes.TextureProperties
+ * @initvalue false
+ * @field x3d
+ * @instance
+ */
+ this.addField_SFBool(ctx, 'generateMipMaps', false);
+
+ },
+ {
+ fieldChanged: function(fieldName)
+ {
+ if (this._vf.hasOwnProperty(fieldName)) {
+ Array.forEach(this._parentNodes, function (texture) {
+ Array.forEach(texture._parentNodes, function (app) {
+ Array.forEach(app._parentNodes, function (shape) {
+ shape._dirty.texture = true;
+ });
+ });
+ });
+
+ this._nameSpace.doc.needRender = true;
+ }
+ }
+ }
+ )
+);
+/** @namespace x3dom.nodeTypes */
+/*
+ * X3DOM JavaScript Library
+ * http://www.x3dom.org
+ *
+ * (C)2009 Fraunhofer IGD, Darmstadt, Germany
+ * Dual licensed under the MIT and GPL
+ */
+
+/* ### X3DTextureNode ### */
+x3dom.registerNodeType(
+ "X3DTextureNode",
+ "Texturing",
+ defineClass(x3dom.nodeTypes.X3DAppearanceChildNode,
+
+ /**
+ * Constructor for X3DTextureNode
+ * @constructs x3dom.nodeTypes.X3DTextureNode
+ * @x3d 3.3
+ * @component Texturing
+ * @status full
+ * @extends x3dom.nodeTypes.X3DAppearanceChildNode
+ * @param {Object} [ctx=null] - context object, containing initial settings like namespace
+ * @classdesc This abstract node type is the base type for all node types which specify sources for texture images.
+ */
+ function (ctx) {
+ x3dom.nodeTypes.X3DTextureNode.superClass.call(this, ctx);
+
+
+ /**
+ * Specifies the channel count of the texture. 0 means the system should figure out the count automatically.
+ * @var {x3dom.fields.SFInt32} origChannelCount
+ * @range [0, inf]
+ * @memberof x3dom.nodeTypes.X3DTextureNode
+ * @initvalue 0
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFInt32(ctx, 'origChannelCount', 0);
+
+ /**
+ * Sets the url to a resource.
+ * @var {x3dom.fields.MFString} url
+ * @memberof x3dom.nodeTypes.X3DTextureNode
+ * @initvalue []
+ * @field x3dom
+ * @instance
+ */
+ this.addField_MFString(ctx, 'url', []);
+
+ /**
+ * Specifies whether the texture is repeated in s direction.
+ * @var {x3dom.fields.SFBool} repeatS
+ * @memberof x3dom.nodeTypes.X3DTextureNode
+ * @initvalue true
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFBool(ctx, 'repeatS', true);
+
+ /**
+ * Specifies whether the texture is repeated in t direction.
+ * @var {x3dom.fields.SFBool} repeatT
+ * @memberof x3dom.nodeTypes.X3DTextureNode
+ * @initvalue true
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFBool(ctx, 'repeatT', true);
+
+ /**
+ * Specifies whether the texture is scaled to the next highest power of two. (Needed, when texture repeat is enabled and texture size is non power of two)
+ * @var {x3dom.fields.SFBool} scale
+ * @memberof x3dom.nodeTypes.X3DTextureNode
+ * @initvalue true
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFBool(ctx, 'scale', true);
+
+ /**
+ * Cross Origin Mode
+ * @var {x3dom.fields.SFString} crossOrigin
+ * @memberof x3dom.nodeTypes.X3DTextureNode
+ * @initvalue ""
+ * @field x3d
+ * @instance
+ */
+ this.addField_SFString(ctx, 'crossOrigin', '');
+
+ /**
+ * Sets a TextureProperty node.
+ * @var {x3dom.fields.SFNode} textureProperties
+ * @memberof x3dom.nodeTypes.X3DTextureNode
+ * @initvalue x3dom.nodeTypes.TextureProperties
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFNode('textureProperties', x3dom.nodeTypes.TextureProperties);
+
+ this._needPerFrameUpdate = false;
+ this._isCanvas = false;
+ this._type = "diffuseMap";
+
+ this._blending = (this._vf.origChannelCount == 1 || this._vf.origChannelCount == 2);
+
+ },
+ {
+ invalidateGLObject: function ()
+ {
+ Array.forEach(this._parentNodes, function (app) {
+ Array.forEach(app._parentNodes, function (shape) {
+ // THINKABOUTME: this is a bit ugly, cleanup more generically
+ if (x3dom.isa(shape, x3dom.nodeTypes.X3DShapeNode)) {
+ shape._dirty.texture = true;
+ }
+ else {
+ // Texture maybe in MultiTexture or CommonSurfaceShader
+ Array.forEach(shape._parentNodes, function (realShape) {
+ if (x3dom.isa(realShape, x3dom.nodeTypes.X3DShapeNode)) {
+ realShape._dirty.texture = true;
+ } else {
+ Array.forEach(realShape._parentNodes, function (realShape2) {
+ if (x3dom.isa(realShape2, x3dom.nodeTypes.X3DShapeNode)) {
+ realShape2._dirty.texture = true;
+ }
+ });
+ }
+ });
+ }
+ });
+ });
+
+ this._nameSpace.doc.needRender = true;
+ },
+
+ parentAdded: function(parent)
+ {
+ Array.forEach(parent._parentNodes, function (shape) {
+ // THINKABOUTME: this is a bit ugly, cleanup more generically
+ if (x3dom.isa(shape, x3dom.nodeTypes.Shape)) {
+ shape._dirty.texture = true;
+ }
+ else {
+ // Texture maybe in MultiTexture or CommonSurfaceShader
+ Array.forEach(shape._parentNodes, function (realShape) {
+ realShape._dirty.texture = true;
+ });
+ }
+ });
+ },
+
+ parentRemoved: function(parent)
+ {
+ Array.forEach(parent._parentNodes, function (shape) {
+ // THINKABOUTME: this is a bit ugly, cleanup more generically
+ if (x3dom.isa(shape, x3dom.nodeTypes.Shape)) {
+ shape._dirty.texture = true;
+ }
+ else {
+ // Texture maybe in MultiTexture or CommonSurfaceShader
+ Array.forEach(shape._parentNodes, function (realShape) {
+ realShape._dirty.texture = true;
+ });
+ }
+ });
+ },
+
+ fieldChanged: function(fieldName)
+ {
+ if (fieldName == "url" || fieldName == "origChannelCount" ||
+ fieldName == "repeatS" || fieldName == "repeatT" ||
+ fieldName == "scale" || fieldName == "crossOrigin")
+ {
+ var that = this;
+
+ Array.forEach(this._parentNodes, function (app) {
+ if (x3dom.isa(app, x3dom.nodeTypes.X3DAppearanceNode)) {
+ app.nodeChanged();
+ Array.forEach(app._parentNodes, function (shape) {
+ shape._dirty.texture = true;
+ });
+ }
+ else if (x3dom.isa(app, x3dom.nodeTypes.MultiTexture)) {
+ Array.forEach(app._parentNodes, function (realApp) {
+ realApp.nodeChanged();
+ Array.forEach(realApp._parentNodes, function (shape) {
+ shape._dirty.texture = true;
+ });
+ });
+ }
+ else if (x3dom.isa(app, x3dom.nodeTypes.ImageGeometry)) {
+ var cf = null;
+ if (that._xmlNode && that._xmlNode.hasAttribute('containerField')) {
+ cf = that._xmlNode.getAttribute('containerField');
+ app._dirty[cf] = true;
+ }
+ }
+ else if (x3dom.nodeTypes.X3DVolumeDataNode !== undefined) {
+ if (x3dom.isa(app, x3dom.nodeTypes.X3DVolumeRenderStyleNode)) {
+ if (that._xmlNode && that._xmlNode.hasAttribute('containerField')) {
+ if(app._volumeDataParent){
+ app._volumeDataParent._dirty.texture = true;
+ }else{
+ //Texture maybe under a ComposedVolumeStyle
+ var volumeDataParent = app._parentNodes[0];
+ while(!x3dom.isa(volumeDataParent, x3dom.nodeTypes.X3DVolumeDataNode) && x3dom.isa(volumeDataParent, x3dom.nodeTypes.X3DNode)){
+ volumeDataParent = volumeDataParent._parentNodes[0];
+ }
+ if(x3dom.isa(volumeDataParent, x3dom.nodeTypes.X3DNode)){
+ volumeDataParent._dirty.texture = true;
+ }
+ }
+ }
+ } else if (x3dom.isa(app, x3dom.nodeTypes.X3DVolumeDataNode)) {
+ if (that._xmlNode && that._xmlNode.hasAttribute('containerField')) {
+ app._dirty.texture = true;
+ }
+ }
+ }
+ });
+ }
+ },
+
+ getTexture: function(pos) {
+ if (pos === 0) {
+ return this;
+ }
+ return null;
+ },
+
+ size: function() {
+ return 1;
+ }
+ }
+ )
+);
+/** @namespace x3dom.nodeTypes */
+/*
+ * X3DOM JavaScript Library
+ * http://www.x3dom.org
+ *
+ * (C)2009 Fraunhofer IGD, Darmstadt, Germany
+ * Dual licensed under the MIT and GPL
+ */
+
+/* ### MultiTexture ### */
+x3dom.registerNodeType(
+ "MultiTexture",
+ "Texturing",
+ defineClass(x3dom.nodeTypes.X3DTextureNode,
+
+ /**
+ * Constructor for MultiTexture
+ * @constructs x3dom.nodeTypes.MultiTexture
+ * @x3d 3.3
+ * @component Texturing
+ * @status experimental
+ * @extends x3dom.nodeTypes.X3DTextureNode
+ * @param {Object} [ctx=null] - context object, containing initial settings like namespace
+ * @classdesc The MultiTexture node specifies the application of several individual textures to a 3D object to achieve a more complex visual effect.
+ * MultiTexture can be used as a value for the texture field in an Appearance node.
+ */
+ function (ctx) {
+ x3dom.nodeTypes.MultiTexture.superClass.call(this, ctx);
+
+
+ /**
+ * The texture field contains a list of texture nodes (e.g., ImageTexture, PixelTexture, and MovieTexture). The texture field may not contain another MultiTexture node.
+ * @var {x3dom.fields.MFNode} texture
+ * @memberof x3dom.nodeTypes.MultiTexture
+ * @initvalue x3dom.nodeTypes.X3DTextureNode
+ * @field x3d
+ * @instance
+ */
+ this.addField_MFNode('texture', x3dom.nodeTypes.X3DTextureNode);
+
+ },
+ {
+ getTexture: function(pos) {
+ if (pos >= 0 && pos < this._cf.texture.nodes.length) {
+ return this._cf.texture.nodes[pos];
+ }
+ return null;
+ },
+
+ getTextures: function() {
+ return this._cf.texture.nodes;
+ },
+
+ size: function() {
+ return this._cf.texture.nodes.length;
+ }
+ }
+ )
+);
+/** @namespace x3dom.nodeTypes */
+/*
+ * X3DOM JavaScript Library
+ * http://www.x3dom.org
+ *
+ * (C)2009 Fraunhofer IGD, Darmstadt, Germany
+ * Dual licensed under the MIT and GPL
+ */
+
+/* ### Texture ### */
+// intermediate layer to avoid instantiating X3DTextureNode in web profile
+x3dom.registerNodeType(
+ "Texture", // X3DTexture2DNode
+ "Texturing",
+ defineClass(x3dom.nodeTypes.X3DTextureNode,
+
+ /**
+ * Constructor for Texture
+ * @constructs x3dom.nodeTypes.Texture
+ * @x3d x.x
+ * @component Texturing
+ * @status experimental
+ * @extends x3dom.nodeTypes.X3DTextureNode
+ * @param {Object} [ctx=null] - context object, containing initial settings like namespace
+ * @classdesc (Abstract) class for 2D Textures.
+ */
+ function (ctx) {
+ x3dom.nodeTypes.Texture.superClass.call(this, ctx);
+
+
+ /**
+ * Specifies whether the children are shown or hidden outside the texture.
+ * @var {x3dom.fields.SFBool} hideChildren
+ * @memberof x3dom.nodeTypes.Texture
+ * @initvalue true
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFBool(ctx, 'hideChildren', true);
+
+ this._video = null;
+ this._intervalID = 0;
+ this._canvas = null;
+
+ },
+ {
+ nodeChanged: function()
+ {
+ if (this._vf.url.length || !this._xmlNode) {
+ return;
+ }
+ x3dom.debug.logInfo("No Texture URL given, searching for &lt;img&gt; elements...");
+ var that = this;
+ try {
+ Array.forEach( this._xmlNode.childNodes, function (childDomNode) {
+ if (childDomNode.nodeType === 1) {
+ var url = childDomNode.getAttribute("src");
+ // For testing: look for <img> element if url empty
+ if (url) {
+ that._vf.url.push(url);
+ x3dom.debug.logInfo(that._vf.url[that._vf.url.length-1]);
+
+ if (childDomNode.localName.toLowerCase() === "video") {
+ that._needPerFrameUpdate = true;
+ //that._video = childDomNode;
+
+ that._video = document.createElement('video');
+ that._video.setAttribute('preload', 'auto');
+ that._video.setAttribute('muted', 'muted');
+ var p = document.getElementsByTagName('body')[0];
+ p.appendChild(that._video);
+ that._video.style.display = "none";
+ that._video.style.visibility = "hidden";
+ }
+ }
+ else if (childDomNode.localName.toLowerCase() === "canvas") {
+ that._needPerFrameUpdate = true;
+ that._isCanvas = true;
+ that._canvas = childDomNode;
+ }
+
+ if (childDomNode.style && that._vf.hideChildren) {
+ childDomNode.style.display = "none";
+ childDomNode.style.visibility = "hidden";
+ }
+ x3dom.debug.logInfo("### Found &lt;"+childDomNode.nodeName+"&gt; tag.");
+ }
+ } );
+ }
+ catch(e) {
+ x3dom.debug.logException(e);
+ }
+ },
+
+ shutdown: function() {
+ if (this._video) {
+ this._video.pause();
+ while (this._video.hasChildNodes()) {
+ this._video.removeChild(this._video.firstChild);
+ }
+ document.body.removeChild(this._video);
+ this._video = null;
+ }
+ }
+ }
+ )
+);
+/** @namespace x3dom.nodeTypes */
+/*
+ * X3DOM JavaScript Library
+ * http://www.x3dom.org
+ *
+ * (C)2009 Fraunhofer IGD, Darmstadt, Germany
+ * Dual licensed under the MIT and GPL
+ */
+
+/* ### RenderedTexture ### */
+x3dom.registerNodeType(
+ "RenderedTexture",
+ "Texturing",
+ defineClass(x3dom.nodeTypes.X3DTextureNode,
+
+ /**
+ * Constructor for RenderedTexture
+ * @constructs x3dom.nodeTypes.RenderedTexture
+ * @x3d x.x
+ * @component Texturing
+ * @extends x3dom.nodeTypes.X3DTextureNode
+ * @param {Object} [ctx=null] - context object, containing initial settings like namespace
+ * @classdesc This extension provides the ability to dynamically render a partial scenegraph to an offscreen texture that can then be used on the geometry of a node.
+ * This can be used in many different ways such as creating mirror effects, inputs to shaders etc.
+ * The purpose of this component is to provide for extended visual effects, but not the complete form of offscreen rendering and buffers that would be available to lower-level rendering APIs.
+ */
+ function (ctx) {
+ x3dom.nodeTypes.RenderedTexture.superClass.call(this, ctx);
+
+ if (ctx)
+ ctx.doc._nodeBag.renderTextures.push(this);
+ else
+ x3dom.debug.logWarning("RenderedTexture: No runtime context found!");
+
+ // Original proposal taken from: http://www.xj3d.org/extensions/render_texture.html
+ // http://doc.instantreality.org/documentation/nodetype/RenderedTexture/?filter=None
+
+
+ /**
+ * Specifies the viewpoint that is used to render the texture content.
+ * @var {x3dom.fields.SFNode} viewpoint
+ * @memberof x3dom.nodeTypes.RenderedTexture
+ * @initvalue x3dom.nodeTypes.X3DViewpointNode
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFNode('viewpoint', x3dom.nodeTypes.X3DViewpointNode);
+
+ /**
+ * Sets a background texture.
+ * @var {x3dom.fields.SFNode} background
+ * @memberof x3dom.nodeTypes.RenderedTexture
+ * @initvalue x3dom.nodeTypes.X3DBackgroundNode
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFNode('background', x3dom.nodeTypes.X3DBackgroundNode);
+
+ /**
+ * Sets a fog node.
+ * @var {x3dom.fields.SFNode} fog
+ * @memberof x3dom.nodeTypes.RenderedTexture
+ * @initvalue x3dom.nodeTypes.X3DFogNode
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFNode('fog', x3dom.nodeTypes.X3DFogNode); //TODO
+
+ /**
+ * Sets a separate, potentially independent, subscene. If unset, the same scene is used.
+ * @var {x3dom.fields.SFNode} scene
+ * @memberof x3dom.nodeTypes.RenderedTexture
+ * @initvalue x3dom.nodeTypes.X3DNode
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFNode('scene', x3dom.nodeTypes.X3DNode);
+
+ /**
+ * Defines a list of sibling nodes that should be ignored.
+ * @var {x3dom.fields.MFNode} excludeNodes
+ * @memberof x3dom.nodeTypes.RenderedTexture
+ * @initvalue x3dom.nodeTypes.X3DNode
+ * @field x3dom
+ * @instance
+ */
+ this.addField_MFNode('excludeNodes', x3dom.nodeTypes.X3DNode);
+
+ /**
+ * Sets the width, height, color components (and number of MRTs).
+ * @var {x3dom.fields.MFInt32} dimensions
+ * @range [0, inf]
+ * @memberof x3dom.nodeTypes.RenderedTexture
+ * @initvalue [128,128,4]
+ * @field x3dom
+ * @instance
+ */
+ this.addField_MFInt32(ctx, 'dimensions', [128, 128, 4]);
+
+ /**
+ * Specifies when the texture is updated.
+ * @var {x3dom.fields.SFString} update
+ * @range ["NONE", "NEXT_FRAME_ONLY", "ALWAYS"]
+ * @memberof x3dom.nodeTypes.RenderedTexture
+ * @initvalue 'NONE'
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFString(ctx, 'update', 'NONE');
+
+
+ /**
+ *Specifies whether normals are shown.
+ * @var {x3dom.fields.SFBool} showNormals
+ * @memberof x3dom.nodeTypes.RenderedTexture
+ * @initvalue false
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFBool(ctx, 'showNormals', false);
+
+ /**
+ * Sets render information for stereo rendering.
+ * @var {x3dom.fields.SFString} stereoMode
+ * @range ["NONE","LEFT_EYE","RIGHT_EYE"]
+ * @memberof x3dom.nodeTypes.RenderedTexture
+ * @initvalue 'NONE'
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFString(ctx, 'stereoMode', 'NONE');
+
+ /**
+ * Sets the eye distance for stereo rendering.
+ * @var {x3dom.fields.SFFloat} interpupillaryDistance
+ * @memberof x3dom.nodeTypes.RenderedTexture
+ * @initvalue 0.064
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFFloat(ctx, 'interpupillaryDistance', 0.064);
+
+ /**
+ * Very experimental field to change between DK1 and DK2.
+ * @var {x3dom.fields.SFFloat} oculusRiftVersion
+ * @memberof x3dom.nodeTypes.RenderedTexture
+ * @initvalue 1
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFBool(ctx, 'oculusRiftVersion', 1);
+
+ this.hScreenSize = (this._vf.oculusRiftVersion == 1) ? 0.14976 : 0.12576;
+ this.vScreenSize = (this._vf.oculusRiftVersion == 1) ? 0.09356 : 0.07074;
+ this.vScreenCenter = this.vScreenSize / 2;
+ this.eyeToScreenDistance = 0.041;
+ this.lensSeparationDistance = 0.0635;
+ this.distortionK = [1.0, 0.22, 0.24, 0.0];
+ //hRes, vRes = 1280 x 800 // DK2: 1920 x 1080
+ //this.lensCenter = 1 - 2 * this.lensSeparationDistance / this.hScreenSize;
+ this.lensCenter = 0.151976495726; // TODO: DK2 ?
+
+ x3dom.debug.assert(this._vf.dimensions.length >= 3,
+ "RenderedTexture.dimensions requires at least 3 entries.");
+ this._clearParents = true;
+ this._needRenderUpdate = true;
+
+ },
+ {
+ nodeChanged: function()
+ {
+ this._clearParents = true;
+ this._needRenderUpdate = true;
+ },
+
+ fieldChanged: function(fieldName)
+ {
+ switch(fieldName)
+ {
+ case "excludeNodes":
+ this._clearParents = true;
+ break;
+ case "update":
+ if (this._vf.update.toUpperCase() == "NEXT_FRAME_ONLY" ||
+ this._vf.update.toUpperCase() == "ALWAYS") {
+ this._needRenderUpdate = true;
+ }
+ break;
+ default:
+ // TODO: dimensions
+ break;
+ }
+ },
+
+ getViewMatrix: function ()
+ {
+ if (this._clearParents && this._cf.excludeNodes.nodes.length) {
+ // FIXME; avoid recursions cleverer and more generic than this
+ // (Problem: nodes in excludeNodes field have this node
+ // as first parent, which leads to a recursion loop in
+ // getCurrentTransform()
+ var that = this;
+
+ Array.forEach(this._cf.excludeNodes.nodes, function(node) {
+ for (var i=0, n=node._parentNodes.length; i < n; i++) {
+ if (node._parentNodes[i] === that) {
+ node._parentNodes.splice(i, 1);
+ node.parentRemoved(that);
+ }
+ }
+ });
+
+ this._clearParents = false;
+ }
+
+ var locScene = this._cf.scene.node;
+ var scene = this._nameSpace.doc._scene;
+ var vbP = scene.getViewpoint();
+ var view = this._cf.viewpoint.node;
+ var ret_mat = null;
+
+ if (view === null || view === vbP) {
+ ret_mat = this._nameSpace.doc._viewarea.getViewMatrix();
+ }
+ else if (locScene && locScene !== scene) {
+ // in case of completely local scene do not transform local viewpoint
+ ret_mat = view.getViewMatrix()
+ }
+ else {
+ var mat_viewpoint = view.getCurrentTransform();
+ ret_mat = view.getViewMatrix().mult(mat_viewpoint.inverse());
+ }
+
+ var stereoMode = this._vf.stereoMode.toUpperCase();
+ if (stereoMode != "NONE") {
+ var d = this._vf.interpupillaryDistance / 2;
+ if (stereoMode == "RIGHT_EYE") {
+ d = -d;
+ }
+ var modifier = new x3dom.fields.SFMatrix4f(
+ 1, 0, 0, d,
+ 0, 1, 0, 0,
+ 0, 0, 1, 0,
+ 0, 0, 0, 1
+ );
+ ret_mat = modifier.mult(ret_mat);
+ }
+
+ return ret_mat;
+ },
+
+ getProjectionMatrix: function()
+ {
+ var doc = this._nameSpace.doc;
+ var vbP = doc._scene.getViewpoint();
+ var view = this._cf.viewpoint.node;
+ var ret_mat = null;
+ var f, w = this._vf.dimensions[0], h = this._vf.dimensions[1];
+ var stereoMode = this._vf.stereoMode.toUpperCase();
+ var stereo = (stereoMode != "NONE");
+
+ if (view === null || view === vbP) {
+ ret_mat = x3dom.fields.SFMatrix4f.copy(doc._viewarea.getProjectionMatrix());
+ if (stereo) {
+ f = 2 * Math.atan(this.vScreenSize / (2 * this.eyeToScreenDistance));
+ f = 1 / Math.tan(f / 2);
+ }
+ else {
+ f = 1 / Math.tan(vbP._vf.fieldOfView / 2);
+ }
+ ret_mat._00 = f / (w / h);
+ ret_mat._11 = f;
+ }
+ else {
+ ret_mat = view.getProjectionMatrix(w / h);
+ }
+
+ if (stereo) {
+ var hp = this.lensCenter;
+ if (stereoMode == "RIGHT_EYE") {
+ hp = -hp;
+ }
+ var modifier = new x3dom.fields.SFMatrix4f(
+ 1, 0, 0, hp,
+ 0, 1, 0, 0,
+ 0, 0, 1, 0,
+ 0, 0, 0, 1
+ );
+ ret_mat = modifier.mult(ret_mat);
+ }
+
+ return ret_mat;
+ },
+
+ getWCtoCCMatrix: function()
+ {
+ var view = this.getViewMatrix();
+ var proj = this.getProjectionMatrix();
+
+ return proj.mult(view);
+ },
+
+ parentRemoved: function(parent)
+ {
+ if (this._parentNodes.length === 0) {
+ var doc = this.findX3DDoc();
+
+ for (var i=0, n=doc._nodeBag.renderTextures.length; i<n; i++) {
+ if (doc._nodeBag.renderTextures[i] === this) {
+ doc._nodeBag.renderTextures.splice(i, 1);
+ }
+ }
+ }
+
+ if (this._cf.scene.node) {
+ this._cf.scene.node.parentRemoved(this);
+ }
+ },
+
+ requirePingPong: function()
+ {
+ return false;
+ }
+ }
+ )
+);
+/** @namespace x3dom.nodeTypes */
+/*
+ * X3DOM JavaScript Library
+ * http://www.x3dom.org
+ *
+ * (C)2009 Fraunhofer IGD, Darmstadt, Germany
+ * Dual licensed under the MIT and GPL
+ */
+
+/* ### RefinementTexture ### */
+x3dom.registerNodeType(
+ "RefinementTexture",
+ "Texturing",
+ defineClass(x3dom.nodeTypes.RenderedTexture,
+
+ /**
+ * Constructor for RefinementTexture
+ * @constructs x3dom.nodeTypes.RefinementTexture
+ * @x3d x.x
+ * @component Texturing
+ * @extends x3dom.nodeTypes.RenderedTexture
+ * @param {Object} [ctx=null] - context object, containing initial settings like namespace
+ */
+ function (ctx) {
+ x3dom.nodeTypes.RefinementTexture.superClass.call(this, ctx);
+
+ /**
+ * Specifies the first stamp texture.
+ * @var {x3dom.fields.SFString} stamp0
+ * @memberof x3dom.nodeTypes.RefinementTexture
+ * @initvalue "gpuii/stamps/0.gif"
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFString(ctx, 'stamp0', "gpuii/stamps/0.gif");
+
+ /**
+ * Specifies the second stamp texture.
+ * @var {x3dom.fields.SFString} stamp1
+ * @memberof x3dom.nodeTypes.RefinementTexture
+ * @initvalue "gpuii/stamps/1.gif"
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFString(ctx, 'stamp1', "gpuii/stamps/1.gif");
+
+ /**
+ * Defines whether texture refinement should be managed by another component.
+ * @var {x3dom.fields.SFBool} autoRefinement
+ * @memberof x3dom.nodeTypes.RefinementTexture
+ * @initvalue true
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFBool(ctx, 'autoRefinement', true);
+
+ /**
+ * Specifies the image format of the dataset.
+ * @var {x3dom.fields.SFString} format
+ * @memberof x3dom.nodeTypes.RefinementTexture
+ * @initvalue 'jpg'
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFString(ctx, 'format', 'jpg');
+
+ /**
+ * Maximum level that should be loaded (if GSM is smaller than on DSL6000)
+ * @var {x3dom.fields.SFInt32} iterations
+ * @memberof x3dom.nodeTypes.RefinementTexture
+ * @initvalue 7
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFInt32(ctx, 'iterations', 7);
+
+ /**
+ * Maximum level that should be loaded (if GSM is smaller than on DSL6000)
+ * @var {x3dom.fields.SFInt32} maxLevel
+ * @memberof x3dom.nodeTypes.RefinementTexture
+ * @initvalue this._vf.iterations
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFInt32(ctx, 'maxLevel', this._vf.iterations);
+
+ if (this._vf.iterations % 2 === 0) {
+ var temp = this._vf.stamp0;
+ this._vf.stamp0 = this._vf.stamp1;
+ this._vf.stamp1 = temp;
+ }
+
+ this._vf.iterations = (this._vf.iterations > 11) ? 11 : this._vf.iterations;
+ this._vf.iterations = (this._vf.iterations < 3) ? 3 : this._vf.iterations;
+ this._vf.maxLevel = (this._vf.maxLevel > 11) ? 11 : this._vf.maxLevel;
+ this._vf.maxLevel = (this._vf.maxLevel < 3) ? 3 : this._vf.maxLevel;
+ this._vf.maxLevel = (this._vf.maxLevel > this._vf.iterations) ? this._vf.iterations : this._vf.maxLevel;
+
+ // Additional parameters to control the refinement mechanism on shader
+ // TODO: optimize
+ var repeatConfig = [
+ {x: 4, y: 8}, {x: 8, y: 8}, {x: 8, y: 16},
+ {x: 16, y: 16}, {x: 16, y: 32}, {x: 32, y: 32},
+ {x: 32, y: 64}, {x: 64, y: 64}, {x: 64, y: 128}
+ ];
+
+ // TODO: optimize
+ this._repeat = new x3dom.fields.SFVec2f(
+ this._vf.dimensions[0] / repeatConfig[this._vf.iterations - 3].x,
+ this._vf.dimensions[1] / repeatConfig[this._vf.iterations - 3].y
+ );
+ this._renderedImage = 0;
+ this._currLoadLevel = 0;
+ this._loadLevel = 1;
+
+ },
+ {
+ nextLevel: function() {
+ if (this._loadLevel < this._vf.maxLevel) {
+ this._loadLevel++;
+ this._nameSpace.doc.needRender = true;
+ }
+ },
+
+ requirePingPong: function() {
+ return (this._currLoadLevel <= this._vf.maxLevel &&
+ this._renderedImage < this._loadLevel);
+ }
+ }
+ )
+);
+/** @namespace x3dom.nodeTypes */
+/*
+ * X3DOM JavaScript Library
+ * http://www.x3dom.org
+ *
+ * (C)2009 Fraunhofer IGD, Darmstadt, Germany
+ * Dual licensed under the MIT and GPL
+ */
+
+/* ### PixelTexture ### */
+x3dom.registerNodeType(
+ "PixelTexture",
+ "Texturing",
+ defineClass(x3dom.nodeTypes.X3DTextureNode,
+
+ /**
+ * Constructor for PixelTexture
+ * @constructs x3dom.nodeTypes.PixelTexture
+ * @x3d 3.3
+ * @component Texturing
+ * @status full
+ * @extends x3dom.nodeTypes.X3DTextureNode
+ * @param {Object} [ctx=null] - context object, containing initial settings like namespace
+ * @classdesc The PixelTexture node defines a 2D image-based texture map as an explicit array of pixel values (image field) and parameters controlling tiling repetition of the texture onto geometry.
+ */
+ function (ctx) {
+ x3dom.nodeTypes.PixelTexture.superClass.call(this, ctx);
+
+
+ /**
+ * The image that delivers the pixel data.
+ * @var {x3dom.fields.SFImage} image
+ * @memberof x3dom.nodeTypes.PixelTexture
+ * @initvalue 0,0,0
+ * @field x3d
+ * @instance
+ */
+ this.addField_SFImage(ctx, 'image', 0, 0, 0);
+
+ },
+ {
+ fieldChanged: function(fieldName)
+ {
+ if (fieldName == "image") {
+ this.invalidateGLObject();
+ }
+ },
+
+ getWidth: function() {
+ return this._vf.image.width;
+ },
+
+ getHeight: function() {
+ return this._vf.image.height;
+ },
+
+ getComponents: function() {
+ return this._vf.image.comp;
+ },
+
+ setPixel: function(x, y, color, update) {
+ update = (update == undefined) ? true : update;
+
+ if (this._x3domTexture) {
+ this._x3domTexture.setPixel(x, y, [
+ color.r*255,
+ color.g*255,
+ color.b*255,
+ color.a*255 ], update );
+ this._vf.image.setPixel(x, y, color);
+ }
+ else
+ {
+ this._vf.image.setPixel(x, y, color);
+ if( update ) {
+ this.invalidateGLObject();
+ }
+ }
+
+ },
+
+ getPixel: function(x, y) {
+ return this._vf.image.getPixel(x, y);
+ },
+
+ setPixels: function(pixels, update) {
+ update = (update == undefined) ? true : update;
+
+ this._vf.image.setPixels(pixels);
+
+ if( update ) {
+ this.invalidateGLObject();
+ }
+ },
+
+ getPixels: function() {
+ return this._vf.image.getPixels();
+ }
+ }
+ )
+);
+/** @namespace x3dom.nodeTypes */
+/*
+ * X3DOM JavaScript Library
+ * http://www.x3dom.org
+ *
+ * (C)2009 Fraunhofer IGD, Darmstadt, Germany
+ * Dual licensed under the MIT and GPL
+ */
+
+/* ### ImageTexture ### */
+x3dom.registerNodeType(
+ "ImageTexture",
+ "Texturing",
+ defineClass(x3dom.nodeTypes.Texture,
+
+ /**
+ * Constructor for ImageTexture
+ * @constructs x3dom.nodeTypes.ImageTexture
+ * @x3d 3.3
+ * @component Texturing
+ * @status full
+ * @extends x3dom.nodeTypes.Texture
+ * @param {Object} [ctx=null] - context object, containing initial settings like namespace
+ * @classdesc ImageTexture maps a 2D-image file onto a geometric shape.
+ * Texture maps have a 2D coordinate system (s, t) horizontal and vertical, with (s, t) values in range [0.0, 1.0] for opposite corners of the image.
+ */
+ function (ctx) {
+ x3dom.nodeTypes.ImageTexture.superClass.call(this, ctx);
+
+ }
+ )
+);
+
+/** @namespace x3dom.nodeTypes */
+/*
+ * X3DOM JavaScript Library
+ * http://www.x3dom.org
+ *
+ * (C)2009 Fraunhofer IGD, Darmstadt, Germany
+ * Dual licensed under the MIT and GPL
+ */
+
+/* ### MovieTexture ### */
+x3dom.registerNodeType(
+ "MovieTexture",
+ "Texturing",
+ defineClass(x3dom.nodeTypes.Texture,
+
+ /**
+ * Constructor for MovieTexture
+ * @constructs x3dom.nodeTypes.MovieTexture
+ * @x3d 3.3
+ * @component Texturing
+ * @status experimental
+ * @extends x3dom.nodeTypes.Texture
+ * @param {Object} [ctx=null] - context object, containing initial settings like namespace
+ * @classdesc The MovieTexture node defines a time dependent texture map (contained in a movie file) and parameters for controlling the movie and the texture mapping.
+ */
+ function (ctx) {
+ x3dom.nodeTypes.MovieTexture.superClass.call(this, ctx);
+
+
+ /**
+ * Specifies whether the playback restarts after finished.
+ * @var {x3dom.fields.SFBool} loop
+ * @memberof x3dom.nodeTypes.MovieTexture
+ * @initvalue false
+ * @field x3d
+ * @instance
+ */
+ this.addField_SFBool(ctx, 'loop', false);
+
+ /**
+ * Specifies the plaback speed.
+ * @var {x3dom.fields.SFFloat} speed
+ * @memberof x3dom.nodeTypes.MovieTexture
+ * @initvalue 1.0
+ * @field x3d
+ * @instance
+ */
+ this.addField_SFFloat(ctx, 'speed', 1.0);
+ // TODO; implement the following fields...
+
+ /**
+ * Sets a time to pause the video.
+ * @var {x3dom.fields.SFTime} pauseTime
+ * @memberof x3dom.nodeTypes.MovieTexture
+ * @initvalue 0
+ * @field x3d
+ * @instance
+ */
+ this.addField_SFTime(ctx, 'pauseTime', 0);
+
+ /**
+ *
+ * @var {x3dom.fields.SFFloat} pitch
+ * @memberof x3dom.nodeTypes.MovieTexture
+ * @initvalue 1.0
+ * @field x3d
+ * @instance
+ */
+ this.addField_SFFloat(ctx, 'pitch', 1.0);
+
+ /**
+ * Sets a time to resume from pause.
+ * @var {x3dom.fields.SFTime} resumeTime
+ * @memberof x3dom.nodeTypes.MovieTexture
+ * @initvalue 0
+ * @field x3d
+ * @instance
+ */
+ this.addField_SFTime(ctx, 'resumeTime', 0);
+
+ /**
+ * Sets a start time for the video.
+ * @var {x3dom.fields.SFTime} startTime
+ * @memberof x3dom.nodeTypes.MovieTexture
+ * @initvalue 0
+ * @field x3d
+ * @instance
+ */
+ this.addField_SFTime(ctx, 'startTime', 0);
+
+ /**
+ * Sets a stop time for the video.
+ * @var {x3dom.fields.SFTime} stopTime
+ * @memberof x3dom.nodeTypes.MovieTexture
+ * @initvalue 0
+ * @field x3d
+ * @instance
+ */
+ this.addField_SFTime(ctx, 'stopTime', 0);
+
+ }
+ )
+);
+/** @namespace x3dom.nodeTypes */
+/*
+ * X3DOM JavaScript Library
+ * http://www.x3dom.org
+ *
+ * (C)2009 Fraunhofer IGD, Darmstadt, Germany
+ * Dual licensed under the MIT and GPL
+ */
+
+/* ### X3DTextureCoordinateNode ### */
+x3dom.registerNodeType(
+ "X3DTextureCoordinateNode",
+ "Texturing",
+ defineClass(x3dom.nodeTypes.X3DGeometricPropertyNode,
+
+ /**
+ * Constructor for X3DTextureCoordinateNode
+ * @constructs x3dom.nodeTypes.X3DTextureCoordinateNode
+ * @x3d 3.3
+ * @component Texturing
+ * @status full
+ * @extends x3dom.nodeTypes.X3DGeometricPropertyNode
+ * @param {Object} [ctx=null] - context object, containing initial settings like namespace
+ * @classdesc This abstract node type is the base type for all node types which specify texture coordinates.
+ */
+ function (ctx) {
+ x3dom.nodeTypes.X3DTextureCoordinateNode.superClass.call(this, ctx);
+
+ },
+ {
+ fieldChanged: function (fieldName) {
+ if (fieldName === "texCoord" || fieldName === "point" ||
+ fieldName === "parameter" || fieldName === "mode")
+ {
+ Array.forEach(this._parentNodes, function (node) {
+ node.fieldChanged("texCoord");
+ });
+ }
+ },
+
+ parentAdded: function(parent) {
+ if (parent._mesh && //parent._cf.coord.node &&
+ parent._cf.texCoord.node !== this) {
+ parent.fieldChanged("texCoord");
+ }
+ }
+ }
+ )
+);
+/** @namespace x3dom.nodeTypes */
+/*
+ * X3DOM JavaScript Library
+ * http://www.x3dom.org
+ *
+ * (C)2009 Fraunhofer IGD, Darmstadt, Germany
+ * Dual licensed under the MIT and GPL
+ */
+
+/* ### TextureCoordinate ### */
+x3dom.registerNodeType(
+ "TextureCoordinate",
+ "Texturing",
+ defineClass(x3dom.nodeTypes.X3DTextureCoordinateNode,
+
+ /**
+ * Constructor for TextureCoordinate
+ * @constructs x3dom.nodeTypes.TextureCoordinate
+ * @x3d 3.3
+ * @component Texturing
+ * @status full
+ * @extends x3dom.nodeTypes.X3DTextureCoordinateNode
+ * @param {Object} [ctx=null] - context object, containing initial settings like namespace
+ * @classdesc The TextureCoordinate node is a geometry property node that specifies a set of 2D texture coordinates used by vertex-based geometry nodes (EXAMPLE IndexedFaceSet and ElevationGrid) to map textures to vertices.
+ */
+ function (ctx) {
+ x3dom.nodeTypes.TextureCoordinate.superClass.call(this, ctx);
+
+
+ /**
+ * Specifies the array of texture coordinates.
+ * @var {x3dom.fields.MFVec2f} point
+ * @memberof x3dom.nodeTypes.TextureCoordinate
+ * @initvalue []
+ * @field x3d
+ * @instance
+ */
+ this.addField_MFVec2f(ctx, 'point', []);
+
+ }
+ )
+);
+/** @namespace x3dom.nodeTypes */
+/*
+ * X3DOM JavaScript Library
+ * http://www.x3dom.org
+ *
+ * (C)2009 Fraunhofer IGD, Darmstadt, Germany
+ * Dual licensed under the MIT and GPL
+ */
+
+/* ### TextureCoordinateGenerator ### */
+x3dom.registerNodeType(
+ "TextureCoordinateGenerator",
+ "Texturing",
+ defineClass(x3dom.nodeTypes.X3DTextureCoordinateNode,
+
+ /**
+ * Constructor for TextureCoordinateGenerator
+ * @constructs x3dom.nodeTypes.TextureCoordinateGenerator
+ * @x3d 3.3
+ * @component Texturing
+ * @status full
+ * @extends x3dom.nodeTypes.X3DTextureCoordinateNode
+ * @param {Object} [ctx=null] - context object, containing initial settings like namespace
+ * @classdesc TextureCoordinateGenerator supports the automatic generation of texture coordinates for geometric shapes.
+ */
+ function (ctx) {
+ x3dom.nodeTypes.TextureCoordinateGenerator.superClass.call(this, ctx);
+
+
+ /**
+ * The mode field describes the algorithm used to compute texture coordinates.
+ * @var {x3dom.fields.SFString} mode
+ * @memberof x3dom.nodeTypes.TextureCoordinateGenerator
+ * @initvalue "SPHERE"
+ * @field x3d
+ * @instance
+ */
+ this.addField_SFString(ctx, 'mode', "SPHERE");
+
+ /**
+ * Specify the parameters. These are mode dependent.
+ * @var {x3dom.fields.MFFloat} parameter
+ * @memberof x3dom.nodeTypes.TextureCoordinateGenerator
+ * @initvalue []
+ * @field x3d
+ * @instance
+ */
+ this.addField_MFFloat(ctx, 'parameter', []);
+
+ }
+ )
+);
+/** @namespace x3dom.nodeTypes */
+/*
+ * X3DOM JavaScript Library
+ * http://www.x3dom.org
+ *
+ * (C)2009 Fraunhofer IGD, Darmstadt, Germany
+ * Dual licensed under the MIT and GPL
+ */
+
+/* ### MultiTextureCoordinate ### */
+x3dom.registerNodeType(
+ "MultiTextureCoordinate",
+ "Texturing",
+ defineClass(x3dom.nodeTypes.X3DTextureCoordinateNode,
+
+ /**
+ * Constructor for MultiTextureCoordinate
+ * @constructs x3dom.nodeTypes.MultiTextureCoordinate
+ * @x3d 3.3
+ * @component Texturing
+ * @status full
+ * @extends x3dom.nodeTypes.X3DTextureCoordinateNode
+ * @param {Object} [ctx=null] - context object, containing initial settings like namespace
+ * @classdesc MultiTextureCoordinate supplies multiple texture coordinates per vertex. This node can be used to set the texture coordinates for the different texture channels.
+ */
+ function (ctx) {
+ x3dom.nodeTypes.MultiTextureCoordinate.superClass.call(this, ctx);
+
+
+ /**
+ * Each entry in the texCoord field may contain a TextureCoordinate or TextureCoordinateGenerator node.
+ * @var {x3dom.fields.MFNode} texCoord
+ * @memberof x3dom.nodeTypes.MultiTextureCoordinate
+ * @initvalue x3dom.nodeTypes.X3DTextureCoordinateNode
+ * @field x3d
+ * @instance
+ */
+ this.addField_MFNode('texCoord', x3dom.nodeTypes.X3DTextureCoordinateNode);
+
+ }
+ )
+);
+
+/** @namespace x3dom.nodeTypes */
+/*
+ * X3DOM JavaScript Library
+ * http://www.x3dom.org
+ *
+ * (C)2009 Fraunhofer IGD, Darmstadt, Germany
+ * Dual licensed under the MIT and GPL
+ */
+
+/* ### ImageTextureAtlas ### */
+x3dom.registerNodeType(
+ "ImageTextureAtlas",
+ "Texturing",
+ defineClass(x3dom.nodeTypes.Texture,
+
+ /**
+ * Constructor for ImageTextureAtlas
+ * @constructs x3dom.nodeTypes.ImageTextureAtlas
+ * @x3d x.x
+ * @component Texturing
+ * @extends x3dom.nodeTypes.Texture
+ * @param {Object} [ctx=null] - context object, containing initial settings like namespace
+ * @classdesc This is a special helper node to represent tiles for volume rendering.
+ */
+ function (ctx) {
+ x3dom.nodeTypes.ImageTextureAtlas.superClass.call(this, ctx);
+
+
+ /**
+ * Specifies the number of slices in the texture atlas.
+ * @var {x3dom.fields.SFInt32} numberOfSlices
+ * @memberof x3dom.nodeTypes.ImageTextureAtlas
+ * @initvalue 0
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFInt32(ctx, 'numberOfSlices', 0);
+
+ /**
+ * Specifies the slices in x.
+ * @var {x3dom.fields.SFInt32} slicesOverX
+ * @memberof x3dom.nodeTypes.ImageTextureAtlas
+ * @initvalue 0
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFInt32(ctx, 'slicesOverX', 0);
+
+ /**
+ * Specifies the slices in y.
+ * @var {x3dom.fields.SFInt32} slicesOverY
+ * @memberof x3dom.nodeTypes.ImageTextureAtlas
+ * @initvalue 0
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFInt32(ctx, 'slicesOverY', 0);
+
+ }
+ )
+);
+/** @namespace x3dom.nodeTypes */
+/*
+ * X3DOM JavaScript Library
+ * http://www.x3dom.org
+ *
+ * (C)2009 Fraunhofer IGD, Darmstadt, Germany
+ * Dual licensed under the MIT and GPL
+ */
+
+/* ### X3DEnvironmentTextureNode ### */
+x3dom.registerNodeType(
+ "X3DEnvironmentTextureNode",
+ "CubeMapTexturing",
+ defineClass(x3dom.nodeTypes.X3DTextureNode,
+
+ /**
+ * Constructor for X3DEnvironmentTextureNode
+ * @constructs x3dom.nodeTypes.X3DEnvironmentTextureNode
+ * @x3d x.x
+ * @component CubeMapTexturing
+ * @status full
+ * @extends x3dom.nodeTypes.X3DTextureNode
+ * @param {Object} [ctx=null] - context object, containing initial settings like namespace
+ * @classdesc This abstract node type is the base type for all node types that specify cubic environment map sources for texture images.
+ */
+ function (ctx) {
+ x3dom.nodeTypes.X3DEnvironmentTextureNode.superClass.call(this, ctx);
+
+ },
+ {
+ getTexUrl: function() {
+ return []; //abstract accessor for gfx
+ },
+
+ getTexSize: function() {
+ return -1; //abstract accessor for gfx
+ }
+ }
+ )
+);
+/** @namespace x3dom.nodeTypes */
+/*
+ * X3DOM JavaScript Library
+ * http://www.x3dom.org
+ *
+ * (C)2009 Fraunhofer IGD, Darmstadt, Germany
+ * Dual licensed under the MIT and GPL
+ */
+
+/* ### ComposedCubeMapTexture ### */
+x3dom.registerNodeType(
+ "ComposedCubeMapTexture",
+ "CubeMapTexturing",
+ defineClass(x3dom.nodeTypes.X3DEnvironmentTextureNode,
+
+ /**
+ * Constructor for ComposedCubeMapTexture
+ * @constructs x3dom.nodeTypes.ComposedCubeMapTexture
+ * @x3d 3.3
+ * @component CubeMapTexturing
+ * @status full
+ * @extends x3dom.nodeTypes.X3DEnvironmentTextureNode
+ * @param {Object} [ctx=null] - context object, containing initial settings like namespace
+ * @classdesc The ComposedCubeMapTexture node defines a cubic environment map source as an explicit set of
+ * images drawn from individual 2D texture nodes.
+ */
+ function (ctx) {
+ x3dom.nodeTypes.ComposedCubeMapTexture.superClass.call(this, ctx);
+
+
+ /**
+ * Texture for the back of the cubemap
+ * @var {x3dom.fields.SFNode} back
+ * @memberof x3dom.nodeTypes.ComposedCubeMapTexture
+ * @initvalue x3dom.nodeTypes.Texture
+ * @field x3d
+ * @instance
+ */
+ this.addField_SFNode('back', x3dom.nodeTypes.Texture);
+
+ /**
+ * Texture for the front of the cubemap
+ * @var {x3dom.fields.SFNode} front
+ * @memberof x3dom.nodeTypes.ComposedCubeMapTexture
+ * @initvalue x3dom.nodeTypes.Texture
+ * @field x3d
+ * @instance
+ */
+ this.addField_SFNode('front', x3dom.nodeTypes.Texture);
+
+ /**
+ * Texture for the bottom of the cubemap
+ * @var {x3dom.fields.SFNode} bottom
+ * @memberof x3dom.nodeTypes.ComposedCubeMapTexture
+ * @initvalue x3dom.nodeTypes.Texture
+ * @field x3d
+ * @instance
+ */
+ this.addField_SFNode('bottom', x3dom.nodeTypes.Texture);
+
+ /**
+ * Texture for the top of the cubemap
+ * @var {x3dom.fields.SFNode} top
+ * @memberof x3dom.nodeTypes.ComposedCubeMapTexture
+ * @initvalue x3dom.nodeTypes.Texture
+ * @field x3d
+ * @instance
+ */
+ this.addField_SFNode('top', x3dom.nodeTypes.Texture);
+
+ /**
+ * Texture for the left side of the cubemap
+ * @var {x3dom.fields.SFNode} left
+ * @memberof x3dom.nodeTypes.ComposedCubeMapTexture
+ * @initvalue x3dom.nodeTypes.Texture
+ * @field x3d
+ * @instance
+ */
+ this.addField_SFNode('left', x3dom.nodeTypes.Texture);
+
+ /**
+ * Texture for the right side of the cubemap
+ * @var {x3dom.fields.SFNode} right
+ * @memberof x3dom.nodeTypes.ComposedCubeMapTexture
+ * @initvalue x3dom.nodeTypes.Texture
+ * @field x3d
+ * @instance
+ */
+ this.addField_SFNode('right', x3dom.nodeTypes.Texture);
+ this._type = "cubeMap";
+
+ },
+ {
+ getTexUrl: function() {
+ return [
+ this._nameSpace.getURL(this._cf.back.node._vf.url[0]),
+ this._nameSpace.getURL(this._cf.front.node._vf.url[0]),
+ this._nameSpace.getURL(this._cf.bottom.node._vf.url[0]),
+ this._nameSpace.getURL(this._cf.top.node._vf.url[0]),
+ this._nameSpace.getURL(this._cf.left.node._vf.url[0]),
+ this._nameSpace.getURL(this._cf.right.node._vf.url[0])
+ ];
+ }
+ }
+ )
+);
+/** @namespace x3dom.nodeTypes */
+/*
+ * X3DOM JavaScript Library
+ * http://www.x3dom.org
+ *
+ * (C)2009 Fraunhofer IGD, Darmstadt, Germany
+ * Dual licensed under the MIT and GPL
+ */
+
+/* ### GeneratedCubeMapTexture ### */
+x3dom.registerNodeType(
+ "GeneratedCubeMapTexture",
+ "CubeMapTexturing",
+ defineClass(x3dom.nodeTypes.X3DEnvironmentTextureNode,
+
+ /**
+ * Constructor for GeneratedCubeMapTexture
+ * @constructs x3dom.nodeTypes.GeneratedCubeMapTexture
+ * @x3d 3.3
+ * @component CubeMapTexturing
+ * @status experimental
+ * @extends x3dom.nodeTypes.X3DEnvironmentTextureNode
+ * @param {Object} [ctx=null] - context object, containing initial settings like namespace
+ * @classdesc The GeneratedCubeMapTexture node defines a cubic environment map that sources its data from
+ * internally generated images, rendered from a virtual situated perspective in the scene.
+ */
+ function (ctx) {
+ x3dom.nodeTypes.GeneratedCubeMapTexture.superClass.call(this, ctx);
+
+
+ /**
+ * The size field indicates the resolution of the generated images in number of pixels per side.
+ * @var {x3dom.fields.SFInt32} size
+ * @memberof x3dom.nodeTypes.GeneratedCubeMapTexture
+ * @initvalue 128
+ * @range (0, inf)
+ * @field x3d
+ * @instance
+ */
+ this.addField_SFInt32(ctx, 'size', 128);
+
+ /**
+ * NOT YET IMPLEMENTED:
+ * The update field can be used to request a regeneration of the texture. Setting this field to "ALWAYS"
+ * will cause the texture to be rendered every frame. A value of "NONE" will stop rendering so that no
+ * further updates are performed even if the contained scene graph changes. When the value is set to
+ * "NEXT_FRAME_ONLY", it is an instruction to render the texture at the end of this frame, and then not
+ * render it again. In this case, the update frame indicator is set to this frame; at the start of the next
+ * frame, the update value shall be automatically set back to "NONE" to indicate that the rendering has already taken place.
+ * @var {x3dom.fields.SFString} update
+ * @memberof x3dom.nodeTypes.GeneratedCubeMapTexture
+ * @initvalue 'NONE'
+ * @field x3d
+ * @instance
+ */
+ this.addField_SFString(ctx, 'update', 'NONE'); // ("NONE"|"NEXT_FRAME_ONLY"|"ALWAYS")
+
+ this._type = "cubeMap";
+ x3dom.debug.logWarning("GeneratedCubeMapTexture NYI"); // TODO; impl. in gfx when fbo type ready
+
+ },
+ {
+ getTexSize: function() {
+ return this._vf.size;
+ }
+ }
+ )
+);
+/** @namespace x3dom.nodeTypes */
+/*
+ * X3DOM JavaScript Library
+ * http://www.x3dom.org
+ *
+ * (C)2009 Fraunhofer IGD, Darmstadt, Germany
+ * Dual licensed under the MIT and GPL
+ */
+
+/* ### Uniform ### */
+x3dom.registerNodeType(
+ "Uniform",
+ "Shaders",
+ defineClass(x3dom.nodeTypes.Field,
+
+ /**
+ * Constructor for Uniform
+ * @constructs x3dom.nodeTypes.Uniform
+ * @x3d x.x
+ * @component Shaders
+ * @extends x3dom.nodeTypes.Field
+ * @param {Object} [ctx=null] - context object, containing initial settings like namespace
+ * @classdesc Node representing a uniform.
+ */
+ function (ctx) {
+ x3dom.nodeTypes.Uniform.superClass.call(this, ctx);
+
+ }
+ )
+);
+/** @namespace x3dom.nodeTypes */
+/*
+ * X3DOM JavaScript Library
+ * http://www.x3dom.org
+ *
+ * (C)2009 Fraunhofer IGD, Darmstadt, Germany
+ * Dual licensed under the MIT and GPL
+ */
+
+/* ### SurfaceShaderTexture ### */
+x3dom.registerNodeType(
+ "SurfaceShaderTexture",
+ "Shaders",
+ defineClass(x3dom.nodeTypes.X3DTextureNode,
+
+ /**
+ * Constructor for SurfaceShaderTexture
+ * @constructs x3dom.nodeTypes.SurfaceShaderTexture
+ * @x3d x.x
+ * @component Shaders
+ * @extends x3dom.nodeTypes.X3DTextureNode
+ * @param {Object} [ctx=null] - context object, containing initial settings like namespace
+ * @classdesc A texture reference that can be used as a child of SurfaceShader.
+ */
+ function (ctx) {
+ x3dom.nodeTypes.SurfaceShaderTexture.superClass.call(this, ctx);
+
+
+ /**
+ * Texture coordinate channel to use for this texture.
+ * @var {x3dom.fields.SFInt32} textureCoordinatesId
+ * @memberof x3dom.nodeTypes.SurfaceShaderTexture
+ * @initvalue 0
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFInt32(ctx, 'textureCoordinatesId', 0);
+
+ /**
+ * Texture channels to use for this texture in the form of a glsl swizzle (e.g. "rgb", "abgr", "a").
+ * "DEFAULT" will use the default channels for the slot ("rgb" for colors and normals, "a" for alpha,
+ * shininess, ...).
+ * @var {x3dom.fields.SFString} channelMask
+ * @memberof x3dom.nodeTypes.SurfaceShaderTexture
+ * @initvalue "DEFAULT"
+ * @range [rgb,abgr,a,..]
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFString(ctx, 'channelMask', "DEFAULT");
+
+ /**
+ * Whether texture contains sRGB content and need to be linearized (NOT IMPLEMENTED!).
+ * @var {x3dom.fields.SFBool} isSRGB
+ * @memberof x3dom.nodeTypes.SurfaceShaderTexture
+ * @initvalue false
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFBool(ctx, 'isSRGB', false);
+
+ /**
+ * The texture to use.
+ * @var {x3dom.fields.SFNode} texture
+ * @memberof x3dom.nodeTypes.SurfaceShaderTexture
+ * @initvalue x3dom.nodeTypes.X3DTextureNode
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFNode('texture', x3dom.nodeTypes.X3DTextureNode);
+
+ /**
+ * An optional texture transform.
+ * @var {x3dom.fields.SFNode} textureTransform
+ * @memberof x3dom.nodeTypes.SurfaceShaderTexture
+ * @initvalue x3dom.nodeTypes.X3DTextureTransformNode
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFNode('textureTransform', x3dom.nodeTypes.X3DTextureTransformNode);
+
+ }
+ )
+);
+/** @namespace x3dom.nodeTypes */
+/*
+ * X3DOM JavaScript Library
+ * http://www.x3dom.org
+ *
+ * (C)2009 Fraunhofer IGD, Darmstadt, Germany
+ * Dual licensed under the MIT and GPL
+ */
+
+/* ### X3DShaderNode ### */
+x3dom.registerNodeType(
+ "X3DShaderNode",
+ "Shaders",
+ defineClass(x3dom.nodeTypes.X3DAppearanceChildNode,
+
+ /**
+ * Constructor for X3DShaderNode
+ * @constructs x3dom.nodeTypes.X3DShaderNode
+ * @x3d 3.3
+ * @component Shaders
+ * @status experimental
+ * @extends x3dom.nodeTypes.X3DAppearanceChildNode
+ * @param {Object} [ctx=null] - context object, containing initial settings like namespace
+ * @classdesc This abstract node type is the base type for all node types that specify a programmable shader.
+ */
+ function (ctx) {
+ x3dom.nodeTypes.X3DShaderNode.superClass.call(this, ctx);
+
+
+ /**
+ * The language field is used to indicate to the browser which shading language is used for the source
+ * file(s). This field may be used as a hint for the browser if the shading language is not immediately
+ * determinable from the source (e.g., a generic MIME type of text/plain is returned). A browser may use
+ * this field for determining which node instance will be selected and to ignore languages that it is not
+ * capable of supporting. Three basic language types are defined for this specification and others may be
+ * optionally supported by a browser.
+ * @var {x3dom.fields.SFString} language
+ * @memberof x3dom.nodeTypes.X3DShaderNode
+ * @initvalue ""
+ * @range ["Cg"|"GLSL"|"HLSL"|...]
+ * @field x3d
+ * @instance
+ */
+ this.addField_SFString(ctx, 'language', "");
+
+ }
+ )
+);
+/** @namespace x3dom.nodeTypes */
+/*
+ * X3DOM JavaScript Library
+ * http://www.x3dom.org
+ *
+ * (C)2009 Fraunhofer IGD, Darmstadt, Germany
+ * Dual licensed under the MIT and GPL
+ */
+
+/* ### CommonSurfaceShader ### */
+x3dom.registerNodeType(
+ "CommonSurfaceShader",
+ "Shaders",
+ defineClass(x3dom.nodeTypes.X3DShaderNode,
+
+ /**
+ * Constructor for CommonSurfaceShader
+ * @constructs x3dom.nodeTypes.CommonSurfaceShader
+ * @x3d x.x
+ * @component Shaders
+ * @extends x3dom.nodeTypes.X3DShaderNode
+ * @param {Object} [ctx=null] - context object, containing initial settings like namespace
+ * @classdesc Implements the Blinn-Phong BRDF with normal mapping and a perfect specular component.
+ */
+ function (ctx) {
+ x3dom.nodeTypes.CommonSurfaceShader.superClass.call(this, ctx);
+
+
+ /**
+ * Texture coordinate channel that contains the tangents in u.
+ * @var {x3dom.fields.SFInt32} tangentTextureCoordinatesId
+ * @memberof x3dom.nodeTypes.CommonSurfaceShader
+ * @initvalue -1
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFInt32(ctx, 'tangentTextureCoordinatesId', -1);
+
+ /**
+ * Texture coordinate channel that contains the tangents in v.
+ * @var {x3dom.fields.SFInt32} binormalTextureCoordinatesId
+ * @memberof x3dom.nodeTypes.CommonSurfaceShader
+ * @initvalue -1
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFInt32(ctx, 'binormalTextureCoordinatesId', -1);
+
+ /**
+ * The value of emissiveTexture is multiplied by this value. If no texture is set, the value is used
+ * directly.
+ * @var {x3dom.fields.SFVec3f} emissiveFactor
+ * @memberof x3dom.nodeTypes.CommonSurfaceShader
+ * @initvalue 0,0,0
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFVec3f(ctx, 'emissiveFactor', 0, 0, 0);
+
+ /**
+ * The texture unit.
+ * @var {x3dom.fields.SFInt32} emissiveTextureId
+ * @memberof x3dom.nodeTypes.CommonSurfaceShader
+ * @initvalue -1
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFInt32(ctx, 'emissiveTextureId', -1);
+
+ /**
+ * Texture coordinate channel to use for emissiveTexture.
+ * @var {x3dom.fields.SFInt32} emissiveTextureCoordinatesId
+ * @memberof x3dom.nodeTypes.CommonSurfaceShader
+ * @initvalue 0
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFInt32(ctx, 'emissiveTextureCoordinatesId', 0);
+
+ /**
+ * ChannelMask for emissiveTexture in the form of a glsl swizzle (e.g. "rgb", "a").
+ * @var {x3dom.fields.SFString} emissiveTextureChannelMask
+ * @memberof x3dom.nodeTypes.CommonSurfaceShader
+ * @initvalue 'rgb'
+ * @range [rgb,a,..]
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFString(ctx, 'emissiveTextureChannelMask', 'rgb');
+
+ /**
+ * The value of ambientTexture is multiplied by this value. If no texture is set, the value is used
+ * directly.
+ * @var {x3dom.fields.SFVec3f} ambientFactor
+ * @memberof x3dom.nodeTypes.CommonSurfaceShader
+ * @initvalue 0.2,0.2,0.2
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFVec3f(ctx, 'ambientFactor', 0.2, 0.2, 0.2);
+
+ /**
+ * The texture unit.
+ * @var {x3dom.fields.SFInt32} ambientTextureId
+ * @memberof x3dom.nodeTypes.CommonSurfaceShader
+ * @initvalue -1
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFInt32(ctx, 'ambientTextureId', -1);
+
+ /**
+ * Texture coordinate channel to use for ambientTexture.
+ * @var {x3dom.fields.SFInt32} ambientTextureCoordinatesId
+ * @memberof x3dom.nodeTypes.CommonSurfaceShader
+ * @initvalue 0
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFInt32(ctx, 'ambientTextureCoordinatesId', 0);
+
+ /**
+ * ChannelMask for ambientTexture in the form of a glsl swizzle (e.g. "rgb", "a").
+ * @var {x3dom.fields.SFString} ambientTextureChannelMask
+ * @memberof x3dom.nodeTypes.CommonSurfaceShader
+ * @initvalue 'rgb'
+ * @range [rgb,a,..]
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFString(ctx, 'ambientTextureChannelMask', 'rgb');
+
+ /**
+ * The value of diffuseTexture is multiplied by this value. If no texture is set, the value is used
+ * directly.
+ * @var {x3dom.fields.SFVec3f} diffuseFactor
+ * @memberof x3dom.nodeTypes.CommonSurfaceShader
+ * @initvalue 0.8,0.8,0.8
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFVec3f(ctx, 'diffuseFactor', 0.8, 0.8, 0.8);
+
+ /**
+ * The texture unit.
+ * @var {x3dom.fields.SFInt32} diffuseTextureId
+ * @memberof x3dom.nodeTypes.CommonSurfaceShader
+ * @initvalue -1
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFInt32(ctx, 'diffuseTextureId', -1);
+
+ /**
+ * Texture coordinate channel to use for diffuseTexture.
+ * @var {x3dom.fields.SFInt32} diffuseTextureCoordinatesId
+ * @memberof x3dom.nodeTypes.CommonSurfaceShader
+ * @initvalue 0
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFInt32(ctx, 'diffuseTextureCoordinatesId', 0);
+
+ /**
+ * ChannelMask for diffuseTexture in the form of a glsl swizzle (e.g. "rgb", "a").
+ * @var {x3dom.fields.SFString} diffuseTextureChannelMask
+ * @memberof x3dom.nodeTypes.CommonSurfaceShader
+ * @initvalue 'rgb'
+ * @range [rgb,a,..]
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFString(ctx, 'diffuseTextureChannelMask', 'rgb');
+
+ /**
+ * The value of specularTexture is multiplied by this value. If no texture is set, the value is used
+ * directly.
+ * @var {x3dom.fields.SFVec3f} specularFactor
+ * @memberof x3dom.nodeTypes.CommonSurfaceShader
+ * @initvalue 0,0,0
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFVec3f(ctx, 'specularFactor', 0, 0, 0);
+
+ /**
+ * The texture unit.
+ * @var {x3dom.fields.SFInt32} specularTextureId
+ * @memberof x3dom.nodeTypes.CommonSurfaceShader
+ * @initvalue -1
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFInt32(ctx, 'specularTextureId', -1);
+
+ /**
+ * Texture coordinate channel to use for specularTexture.
+ * @var {x3dom.fields.SFInt32} specularTextureCoordinatesId
+ * @memberof x3dom.nodeTypes.CommonSurfaceShader
+ * @initvalue 0
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFInt32(ctx, 'specularTextureCoordinatesId', 0);
+
+ /**
+ * ChannelMask for specularTexture in the form of a glsl swizzle (e.g. "rgb", "a").
+ * @var {x3dom.fields.SFString} specularTextureChannelMask
+ * @memberof x3dom.nodeTypes.CommonSurfaceShader
+ * @initvalue 'rgb'
+ * @range [rgb,a,..]
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFString(ctx, 'specularTextureChannelMask', 'rgb');
+
+ /**
+ * The value of shininessTexture is multiplied by this value. If no texture is set, the value is used
+ * directly.
+ * @var {x3dom.fields.SFFloat} shininessFactor
+ * @memberof x3dom.nodeTypes.CommonSurfaceShader
+ * @initvalue 0.2
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFFloat(ctx, 'shininessFactor', 0.2);
+
+ /**
+ * The texture unit.
+ * @var {x3dom.fields.SFInt32} shininessTextureId
+ * @memberof x3dom.nodeTypes.CommonSurfaceShader
+ * @initvalue -1
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFInt32(ctx, 'shininessTextureId', -1);
+
+ /**
+ * Texture coordinate channel to use for shininessTexture.
+ * @var {x3dom.fields.SFInt32} shininessTextureCoordinatesId
+ * @memberof x3dom.nodeTypes.CommonSurfaceShader
+ * @initvalue 0
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFInt32(ctx, 'shininessTextureCoordinatesId', 0);
+
+ /**
+ * ChannelMask for shininessTexture in the form of a glsl swizzle (e.g. "rgb", "a").
+ * @var {x3dom.fields.SFString} shininessTextureChannelMask
+ * @memberof x3dom.nodeTypes.CommonSurfaceShader
+ * @initvalue 'a'
+ * @range [rgb,a,..]
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFString(ctx, 'shininessTextureChannelMask', 'a');
+
+ /**
+ * How normals are stored in normalTexture. Currently only "UNORM" (each component packed into a
+ * [0,1] color channel) is supported.
+ * @var {x3dom.fields.SFString} normalFormat
+ * @memberof x3dom.nodeTypes.CommonSurfaceShader
+ * @initvalue 'UNORM'
+ * @range [UNORM]
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFString(ctx, 'normalFormat', 'UNORM');
+
+ /**
+ * Space in which normals in normalTexture are defined. Currently only "TANGENT" (a default tangent space
+ * normal map) is supported.
+ * @var {x3dom.fields.SFString} normalSpace
+ * @memberof x3dom.nodeTypes.CommonSurfaceShader
+ * @initvalue 'TANGENT'
+ * @range [TANGENT]
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFString(ctx, 'normalSpace', 'TANGENT');
+
+ /**
+ * The texture unit.
+ * @var {x3dom.fields.SFInt32} normalTextureId
+ * @memberof x3dom.nodeTypes.CommonSurfaceShader
+ * @initvalue -1
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFInt32(ctx, 'normalTextureId', -1);
+
+ /**
+ * Texture coordinate channel to use for normalTexture.
+ * @var {x3dom.fields.SFInt32} normalTextureCoordinatesId
+ * @memberof x3dom.nodeTypes.CommonSurfaceShader
+ * @initvalue 0
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFInt32(ctx, 'normalTextureCoordinatesId', 0);
+
+ /**
+ * ChannelMask for normalTexture in the form of a glsl swizzle (e.g. "rgb", "a").
+ * @var {x3dom.fields.SFString} normalTextureChannelMask
+ * @memberof x3dom.nodeTypes.CommonSurfaceShader
+ * @initvalue 'rgb'
+ * @range [rgb,a,..]
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFString(ctx, 'normalTextureChannelMask', 'rgb');
+
+ /**
+ * The value of reflectionTexture is multiplied by this value. If no texture is set, the value is used
+ * directly.
+ * @var {x3dom.fields.SFVec3f} reflectionFactor
+ * @memberof x3dom.nodeTypes.CommonSurfaceShader
+ * @initvalue 0,0,0
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFVec3f(ctx, 'reflectionFactor', 0, 0, 0);
+
+ /**
+ * The texture unit.
+ * @var {x3dom.fields.SFInt32} reflectionTextureId
+ * @memberof x3dom.nodeTypes.CommonSurfaceShader
+ * @initvalue -1
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFInt32(ctx, 'reflectionTextureId', -1);
+
+ /**
+ * Texture coordinate channel to use for reflectionTexture.
+ * @var {x3dom.fields.SFInt32} reflectionTextureCoordinatesId
+ * @memberof x3dom.nodeTypes.CommonSurfaceShader
+ * @initvalue 0
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFInt32(ctx, 'reflectionTextureCoordinatesId', 0);
+
+ /**
+ * ChannelMask for reflectionTexture in the form of a glsl swizzle (e.g. "rgb", "a").
+ * @var {x3dom.fields.SFString} reflectionTextureChannelMask
+ * @memberof x3dom.nodeTypes.CommonSurfaceShader
+ * @initvalue 'rgb'
+ * @range [rgb,a,..]
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFString(ctx, 'reflectionTextureChannelMask', 'rgb');
+
+ /**
+ * The value of transmissionTexture is multiplied by this value. If no texture is set, the value is used
+ * directly.
+ * @var {x3dom.fields.SFVec3f} transmissionFactor
+ * @memberof x3dom.nodeTypes.CommonSurfaceShader
+ * @initvalue 0,0,0
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFVec3f(ctx, 'transmissionFactor', 0, 0, 0);
+
+ /**
+ * The texture unit.
+ * @var {x3dom.fields.SFInt32} transmissionTextureId
+ * @memberof x3dom.nodeTypes.CommonSurfaceShader
+ * @initvalue -1
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFInt32(ctx, 'transmissionTextureId', -1);
+
+ /**
+ * Texture coordinate channel to use for transmissionTexture.
+ * @var {x3dom.fields.SFInt32} transmissionTextureCoordinatesId
+ * @memberof x3dom.nodeTypes.CommonSurfaceShader
+ * @initvalue 0
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFInt32(ctx, 'transmissionTextureCoordinatesId', 0);
+
+ /**
+ * ChannelMask for transmissionTexture in the form of a glsl swizzle (e.g. "rgb", "a").
+ * @var {x3dom.fields.SFString} transmissionTextureChannelMask
+ * @memberof x3dom.nodeTypes.CommonSurfaceShader
+ * @initvalue 'rgb'
+ * @range [rgb,a,..]
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFString(ctx, 'transmissionTextureChannelMask', 'rgb');
+
+ /**
+ * The value of environmentTexture is multiplied by this value. If no texture is set, the value is used
+ * directly.
+ * @var {x3dom.fields.SFVec3f} environmentFactor
+ * @memberof x3dom.nodeTypes.CommonSurfaceShader
+ * @initvalue 1,1,1
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFVec3f(ctx, 'environmentFactor', 1, 1, 1);
+
+ /**
+ * The texture unit.
+ * @var {x3dom.fields.SFInt32} environmentTextureId
+ * @memberof x3dom.nodeTypes.CommonSurfaceShader
+ * @initvalue -1
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFInt32(ctx, 'environmentTextureId', -1);
+
+ /**
+ * [Currently not used, coordinates are computed in shader.]
+ * @var {x3dom.fields.SFInt32} environmentTextureCoordinatesId
+ * @memberof x3dom.nodeTypes.CommonSurfaceShader
+ * @initvalue 0
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFInt32(ctx, 'environmentTextureCoordinatesId', 0);
+
+ /**
+ * ChannelMask for environmentTexture in the form of a glsl swizzle (e.g. "rgb", "a").
+ * @var {x3dom.fields.SFString} environmentTextureChannelMask
+ * @memberof x3dom.nodeTypes.CommonSurfaceShader
+ * @initvalue 'rgb'
+ * @range [rgb,a,..]
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFString(ctx, 'environmentTextureChannelMask', 'rgb');
+
+ /**
+ * Relative IOR for perfect specular component.
+ * @var {x3dom.fields.SFFloat} relativeIndexOfRefraction
+ * @memberof x3dom.nodeTypes.CommonSurfaceShader
+ * @initvalue 1
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFFloat(ctx, 'relativeIndexOfRefraction', 1);
+
+ /**
+ * To what degree the Fresnel equation for dielectrics should be used to blend the perfect specular
+ * reflection and transmission.
+ * @var {x3dom.fields.SFFloat} fresnelBlend
+ * @memberof x3dom.nodeTypes.CommonSurfaceShader
+ * @initvalue 0
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFFloat(ctx, 'fresnelBlend', 0);
+
+ /**
+ * Axis along which the vertices are displacement
+ * @var {x3dom.fields.SFString} displacementAxis
+ * @memberof x3dom.nodeTypes.CommonSurfaceShader
+ * @initvalue 'y'
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFString(ctx, 'displacementAxis', 'y');
+
+ /**
+ * Factor for the displacement.
+ * @var {x3dom.fields.SFFloat} displacementFactor
+ * @memberof x3dom.nodeTypes.CommonSurfaceShader
+ * @initvalue 255.0
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFFloat(ctx, 'displacementFactor', 255.0);
+
+ /**
+ * The texture unit.
+ * @var {x3dom.fields.SFInt32} displacementTextureId
+ * @memberof x3dom.nodeTypes.CommonSurfaceShader
+ * @initvalue -1
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFInt32(ctx, 'displacementTextureId', -1);
+
+ /**
+ * Texture coordinate channel to use for displacementTexture.
+ * @var {x3dom.fields.SFInt32} displacementTextureCoordinatesId
+ * @memberof x3dom.nodeTypes.CommonSurfaceShader
+ * @initvalue 0
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFInt32(ctx, 'displacementTextureCoordinatesId', 0);
+
+ /**
+ * Texture containing emissive component.
+ * @var {x3dom.fields.SFNode} emissiveTexture
+ * @memberof x3dom.nodeTypes.CommonSurfaceShader
+ * @initvalue x3dom.nodeTypes.X3DTextureNode
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFNode('emissiveTexture', x3dom.nodeTypes.X3DTextureNode);
+
+ /**
+ * Texture containing ambient component.
+ * @var {x3dom.fields.SFNode} ambientTexture
+ * @memberof x3dom.nodeTypes.CommonSurfaceShader
+ * @initvalue x3dom.nodeTypes.X3DTextureNode
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFNode('ambientTexture', x3dom.nodeTypes.X3DTextureNode);
+
+ /**
+ * Texture containing diffuse component.
+ * @var {x3dom.fields.SFNode} diffuseTexture
+ * @memberof x3dom.nodeTypes.CommonSurfaceShader
+ * @initvalue x3dom.nodeTypes.X3DTextureNode
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFNode('diffuseTexture', x3dom.nodeTypes.X3DTextureNode);
+
+ /**
+ * Texture containing specular component.
+ * @var {x3dom.fields.SFNode} specularTexture
+ * @memberof x3dom.nodeTypes.CommonSurfaceShader
+ * @initvalue x3dom.nodeTypes.X3DTextureNode
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFNode('specularTexture', x3dom.nodeTypes.X3DTextureNode);
+
+ /**
+ * Texture containing shininess component.
+ * @var {x3dom.fields.SFNode} shininessTexture
+ * @memberof x3dom.nodeTypes.CommonSurfaceShader
+ * @initvalue x3dom.nodeTypes.X3DTextureNode
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFNode('shininessTexture', x3dom.nodeTypes.X3DTextureNode);
+
+ /**
+ * Texture containing normal component for normal mapping.
+ * @var {x3dom.fields.SFNode} normalTexture
+ * @memberof x3dom.nodeTypes.CommonSurfaceShader
+ * @initvalue x3dom.nodeTypes.X3DTextureNode
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFNode('normalTexture', x3dom.nodeTypes.X3DTextureNode);
+
+ /**
+ * Texture containing reflection component.
+ * @var {x3dom.fields.SFNode} reflectionTexture
+ * @memberof x3dom.nodeTypes.CommonSurfaceShader
+ * @initvalue x3dom.nodeTypes.X3DTextureNode
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFNode('reflectionTexture', x3dom.nodeTypes.X3DTextureNode);
+
+ /**
+ * Texture containing transmission component.
+ * @var {x3dom.fields.SFNode} transmissionTexture
+ * @memberof x3dom.nodeTypes.CommonSurfaceShader
+ * @initvalue x3dom.nodeTypes.X3DTextureNode
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFNode('transmissionTexture', x3dom.nodeTypes.X3DTextureNode);
+
+ /**
+ * Cube texture containing the environment for perfect specular reflection and transmission.
+ * @var {x3dom.fields.SFNode} environmentTexture
+ * @memberof x3dom.nodeTypes.CommonSurfaceShader
+ * @initvalue x3dom.nodeTypes.X3DTextureNode
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFNode('environmentTexture', x3dom.nodeTypes.X3DTextureNode);
+
+ /**
+ * Texture containing displacement component.
+ * @var {x3dom.fields.SFNode} displacementTexture
+ * @memberof x3dom.nodeTypes.CommonSurfaceShader
+ * @initvalue x3dom.nodeTypes.X3DTextureNode
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFNode('displacementTexture', x3dom.nodeTypes.X3DTextureNode);
+
+ /**
+ * Texture containing diffuse displacement component.
+ * @var {x3dom.fields.SFNode} diffuseDisplacementTexture
+ * @memberof x3dom.nodeTypes.CommonSurfaceShader
+ * @initvalue x3dom.nodeTypes.X3DTextureNode
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFNode('diffuseDisplacementTexture', x3dom.nodeTypes.X3DTextureNode);
+
+ /**
+ * Multi diffuse alpha texture.
+ * @var {x3dom.fields.SFNode} multiDiffuseAlphaTexture
+ * @memberof x3dom.nodeTypes.CommonSurfaceShader
+ * @initvalue x3dom.nodeTypes.X3DTextureNode
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFNode('multiDiffuseAlphaTexture', x3dom.nodeTypes.X3DTextureNode);
+
+ /**
+ * Multi specular shininess texture.
+ * @var {x3dom.fields.SFNode} multiSpecularShininessTexture
+ * @memberof x3dom.nodeTypes.CommonSurfaceShader
+ * @initvalue x3dom.nodeTypes.X3DTextureNode
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFNode('multiSpecularShininessTexture', x3dom.nodeTypes.X3DTextureNode);
+
+ /**
+ * Multi emissive ambientIntensity texture.
+ * @var {x3dom.fields.SFNode} multiEmmisiveAmbientIntensityTexture
+ * @memberof x3dom.nodeTypes.CommonSurfaceShader
+ * @initvalue x3dom.nodeTypes.X3DTextureNode
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFNode('multiEmissiveAmbientTexture', x3dom.nodeTypes.X3DTextureNode);
+
+ /**
+ * Multi visibility texture.
+ * @var {x3dom.fields.SFNode} multiVisibilityTexture
+ * @memberof x3dom.nodeTypes.CommonSurfaceShader
+ * @initvalue x3dom.nodeTypes.X3DTextureNode
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFNode('multiVisibilityTexture', x3dom.nodeTypes.X3DTextureNode);
+
+
+ //this.addField_MFBool(ctx, 'textureTransformEnabled', []); // MFBool NYI
+
+ /**
+ * scale to apply to normal sampled from normalTexture
+ * @var {x3dom.fields.SFVec3f} normalScale
+ * @memberof x3dom.nodeTypes.CommonSurfaceShader
+ * @initvalue 2,2,2
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFVec3f(ctx, 'normalScale', 2, 2, 2);
+
+ /**
+ * Bias to apply to normal sampled from normalTexture
+ * @var {x3dom.fields.SFVec3f} normalBias
+ * @memberof x3dom.nodeTypes.CommonSurfaceShader
+ * @initvalue -1,-1,-1
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFVec3f(ctx, 'normalBias', -1, -1, -1);
+
+ /**
+ * The value of alphaTexture is multiplied by this value. If no texture is set, the value is used directly.
+ * @var {x3dom.fields.SFFloat} alphaFactor
+ * @memberof x3dom.nodeTypes.CommonSurfaceShader
+ * @initvalue 1
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFFloat(ctx, 'alphaFactor', 1);
+
+ /**
+ * If true, (1-sampledValue) is used as alpha. If false the sampled value is used.
+ * @var {x3dom.fields.SFBool} invertAlphaTexture
+ * @memberof x3dom.nodeTypes.CommonSurfaceShader
+ * @initvalue false
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFBool(ctx, 'invertAlphaTexture', false);
+
+ /**
+ * The texture unit.
+ * @var {x3dom.fields.SFInt32} alphaTextureId
+ * @memberof x3dom.nodeTypes.CommonSurfaceShader
+ * @initvalue -1
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFInt32(ctx, 'alphaTextureId', -1);
+
+ /**
+ * Texture coordinate channel to use for alphaTexture.
+ * @var {x3dom.fields.SFInt32} alphaTextureCoordinatesId
+ * @memberof x3dom.nodeTypes.CommonSurfaceShader
+ * @initvalue 0
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFInt32(ctx, 'alphaTextureCoordinatesId', 0);
+
+ /**
+ * ChannelMask for alphaTexture in the form of a glsl swizzle (e.g. "rgb", "a").
+ * @var {x3dom.fields.SFString} alphaTextureChannelMask
+ * @memberof x3dom.nodeTypes.CommonSurfaceShader
+ * @initvalue 'a'
+ * @range [a,rgb,..]
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFString(ctx, 'alphaTextureChannelMask', 'a');
+
+ /**
+ * Texture containing alpha component.
+ * @var {x3dom.fields.SFNode} alphaTexture
+ * @memberof x3dom.nodeTypes.CommonSurfaceShader
+ * @initvalue x3dom.nodeTypes.X3DTextureNode
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFNode('alphaTexture', x3dom.nodeTypes.X3DTextureNode);
+
+ this._dirty = {
+ // TODO; cp. Shape, allow for dynamic texture updates in gfx
+ };
+
+ },
+ {
+ getDiffuseMap: function()
+ {
+ if(this._cf.diffuseTexture.node) {
+ if (x3dom.isa(this._cf.diffuseTexture.node, x3dom.nodeTypes.SurfaceShaderTexture)) {
+ this._cf.diffuseTexture.node._cf.texture.node._type = "diffuseMap";
+ return this._cf.diffuseTexture.node._cf.texture.node;
+ } else {
+ this._cf.diffuseTexture.node._type = "diffuseMap";
+ return this._cf.diffuseTexture.node;
+ }
+ } else {
+ return null;
+ }
+ },
+
+ getNormalMap: function()
+ {
+ if(this._cf.normalTexture.node) {
+ if (x3dom.isa(this._cf.normalTexture.node, x3dom.nodeTypes.SurfaceShaderTexture)) {
+ this._cf.normalTexture.node._cf.texture.node._type = "normalMap";
+ return this._cf.normalTexture.node._cf.texture.node;
+ } else {
+ this._cf.normalTexture.node._type = "normalMap";
+ return this._cf.normalTexture.node;
+ }
+ } else {
+ return null;
+ }
+ },
+
+ getAmbientMap: function()
+ {
+ if(this._cf.ambientTexture.node) {
+ if (x3dom.isa(this._cf.ambientTexture.node, x3dom.nodeTypes.SurfaceShaderTexture)) {
+ this._cf.ambientTexture.node._cf.texture.node._type = "ambientMap";
+ return this._cf.ambientTexture.node._cf.texture.node;
+ } else {
+ this._cf.ambientTexture.node._type = "ambientMap";
+ return this._cf.ambientTexture.node;
+ }
+ } else {
+ return null;
+ }
+ },
+
+ getSpecularMap: function()
+ {
+ if(this._cf.specularTexture.node) {
+ if (x3dom.isa(this._cf.specularTexture.node, x3dom.nodeTypes.SurfaceShaderTexture)) {
+ this._cf.specularTexture.node._cf.texture.node._type = "specularMap";
+ return this._cf.specularTexture.node._cf.texture.node;
+ } else {
+ this._cf.specularTexture.node._type = "specularMap";
+ return this._cf.specularTexture.node;
+ }
+ } else {
+ return null;
+ }
+ },
+
+ getShininessMap: function()
+ {
+ if(this._cf.shininessTexture.node) {
+ if (x3dom.isa(this._cf.shininessTexture.node, x3dom.nodeTypes.SurfaceShaderTexture)) {
+ this._cf.shininessTexture.node._cf.texture.node._type = "shininessMap";
+ return this._cf.shininessTexture.node._cf.texture.node;
+ } else {
+ this._cf.shininessTexture.node._type = "shininessMap";
+ return this._cf.shininessTexture.node;
+ }
+ } else {
+ return null;
+ }
+ },
+
+ getAlphaMap: function()
+ {
+ if(this._cf.alphaTexture.node) {
+ if (x3dom.isa(this._cf.alphaTexture.node, x3dom.nodeTypes.SurfaceShaderTexture)) {
+ this._cf.alphaTexture.node._cf.texture.node._type = "alphaMap";
+ return this._cf.alphaTexture.node._cf.texture.node;
+ } else {
+ this._cf.alphaTexture.node._type = "alphaMap";
+ return this._cf.alphaTexture.node;
+ }
+ } else {
+ return null;
+ }
+ },
+
+ getDisplacementMap: function()
+ {
+ if(this._cf.displacementTexture.node) {
+ if (x3dom.isa(this._cf.displacementTexture.node, x3dom.nodeTypes.SurfaceShaderTexture)) {
+ this._cf.displacementTexture.node._cf.texture.node._type = "displacementMap";
+ return this._cf.displacementTexture.node._cf.texture.node;
+ } else {
+ this._cf.displacementTexture.node._type = "displacementMap";
+ return this._cf.displacementTexture.node;
+ }
+ } else {
+ return null;
+ }
+ },
+
+ getDiffuseDisplacementMap: function()
+ {
+ if(this._cf.diffuseDisplacementTexture.node) {
+ if (x3dom.isa(this._cf.diffuseDisplacementTexture.node, x3dom.nodeTypes.SurfaceShaderTexture)) {
+ this._cf.diffuseDisplacementTexture.node._cf.texture.node._type = "diffuseDisplacementMap";
+ return this._cf.diffuseDisplacementTexture.node._cf.texture.node;
+ } else {
+ this._cf.diffuseDisplacementTexture.node._type = "diffuseDisplacementMap";
+ return this._cf.diffuseDisplacementTexture.node;
+ }
+ } else {
+ return null;
+ }
+ },
+
+ getMultiDiffuseAlphaMap: function()
+ {
+ if(this._cf.multiDiffuseAlphaTexture.node) {
+ if (x3dom.isa(this._cf.multiDiffuseAlphaTexture.node, x3dom.nodeTypes.SurfaceShaderTexture)) {
+ this._cf.multiDiffuseAlphaTexture.node._cf.texture.node._type = "multiDiffuseAlphaMap";
+ return this._cf.multiDiffuseAlphaTexture.node._cf.texture.node;
+ } else {
+ this._cf.multiDiffuseAlphaTexture.node._type = "multiDiffuseAlphaMap";
+ return this._cf.multiDiffuseAlphaTexture.node;
+ }
+ } else {
+ return null;
+ }
+ },
+
+ getMultiEmissiveAmbientMap: function()
+ {
+ if(this._cf.multiEmissiveAmbientTexture.node) {
+ if (x3dom.isa(this._cf.multiEmissiveAmbientTexture.node, x3dom.nodeTypes.SurfaceShaderTexture)) {
+ this._cf.multiEmissiveAmbientTexture.node._cf.texture.node._type = "multiEmissiveAmbientMap";
+ return this._cf.multiEmissiveAmbientTexture.node._cf.texture.node;
+ } else {
+ this._cf.multiEmissiveAmbientTexture.node._type = "multiEmissiveAmbientMap";
+ return this._cf.multiEmissiveAmbientTexture.node;
+ }
+ } else {
+ return null;
+ }
+ },
+
+ getMultiSpecularShininessMap: function()
+ {
+ if(this._cf.multiSpecularShininessTexture.node) {
+ if (x3dom.isa(this._cf.multiSpecularShininessTexture.node, x3dom.nodeTypes.SurfaceShaderTexture)) {
+ this._cf.multiSpecularShininessTexture.node._cf.texture.node._type = "multiSpecularShininessMap";
+ return this._cf.multiSpecularShininessTexture.node._cf.texture.node;
+ } else {
+ this._cf.multiSpecularShininessTexture.node._type = "multiSpecularShininessMap";
+ return this._cf.multiSpecularShininessTexture.node;
+ }
+ } else {
+ return null;
+ }
+ },
+
+ getMultiVisibilityMap: function()
+ {
+ if(this._cf.multiVisibilityTexture.node) {
+ if (x3dom.isa(this._cf.multiVisibilityTexture.node, x3dom.nodeTypes.SurfaceShaderTexture)) {
+ this._cf.multiVisibilityTexture.node._cf.texture.node._type = "multiVisibilityMap";
+ return this._cf.multiVisibilityTexture.node._cf.texture.node;
+ } else {
+ this._cf.multiVisibilityTexture.node._type = "multiVisibilityMap";
+ return this._cf.multiVisibilityTexture.node;
+ }
+ } else {
+ return null;
+ }
+ },
+
+ getTextures: function()
+ {
+ var textures = [];
+
+ var diff = this.getDiffuseMap();
+ if(diff) textures.push(diff);
+
+ var norm = this.getNormalMap();
+ if(norm) textures.push(norm);
+
+ var spec = this.getSpecularMap();
+ if(spec) textures.push(spec);
+
+ var shin = this.getShininessMap();
+ if(shin) textures.push(shin);
+
+ var displacement = this.getDisplacementMap();
+ if(displacement) textures.push(displacement);
+
+ var diffuseDisplacement = this.getDiffuseDisplacementMap();
+ if(diffuseDisplacement) textures.push(diffuseDisplacement);
+
+ var multiDiffuseAlpha = this.getMultiDiffuseAlphaMap();
+ if(multiDiffuseAlpha) textures.push(multiDiffuseAlpha);
+
+ var multiEmissiveAmbient = this.getMultiEmissiveAmbientMap();
+ if(multiEmissiveAmbient) textures.push(multiEmissiveAmbient);
+
+ var multiSpecularShininess = this.getMultiSpecularShininessMap();
+ if(multiSpecularShininess) textures.push(multiSpecularShininess);
+
+ var multiVisibility = this.getMultiVisibilityMap();
+ if(multiVisibility) textures.push(multiVisibility);
+
+ return textures;
+ }
+ }
+ )
+);
+/** @namespace x3dom.nodeTypes */
+/*
+ * X3DOM JavaScript Library
+ * http://www.x3dom.org
+ *
+ * (C)2009 Fraunhofer IGD, Darmstadt, Germany
+ * Dual licensed under the MIT and GPL
+ */
+
+/* ### ComposedShader ### */
+x3dom.registerNodeType(
+ "ComposedShader",
+ "Shaders",
+ defineClass(x3dom.nodeTypes.X3DShaderNode,
+
+ /**
+ * Constructor for ComposedShader
+ * @constructs x3dom.nodeTypes.ComposedShader
+ * @x3d 3.3
+ * @component Shaders
+ * @status experimental
+ * @extends x3dom.nodeTypes.X3DShaderNode
+ * @param {Object} [ctx=null] - context object, containing initial settings like namespace
+ * @classdesc The ComposedShader node defines a shader where the individual source files are not individually
+ * programmable. All access to the shading capabilities is defined through a single interface that applies to
+ * all parts.
+ */
+ function (ctx) {
+ x3dom.nodeTypes.ComposedShader.superClass.call(this, ctx);
+
+
+ /**
+ * Contains all fields of shader parts.
+ * @var {x3dom.fields.MFNode} fields
+ * @memberof x3dom.nodeTypes.ComposedShader
+ * @initvalue x3dom.nodeTypes.Field
+ * @field x3d
+ * @instance
+ */
+ this.addField_MFNode('fields', x3dom.nodeTypes.Field);
+
+ /**
+ * List of shader parts.
+ * @var {x3dom.fields.MFNode} parts
+ * @memberof x3dom.nodeTypes.ComposedShader
+ * @initvalue x3dom.nodeTypes.ShaderPart
+ * @field x3d
+ * @instance
+ */
+ this.addField_MFNode('parts', x3dom.nodeTypes.ShaderPart);
+
+ // shortcut to shader parts
+ this._vertex = null;
+ this._fragment = null;
+ this._id = null;
+
+ if (!x3dom.nodeTypes.ComposedShader.ShaderInfoMsgShown) {
+ x3dom.debug.logInfo("Current ComposedShader node implementation limitations:\n" +
+ "Vertex attributes (if given in the standard X3D fields 'coord', 'color', " +
+ "'normal', 'texCoord'), matrices and texture are provided as follows...\n" +
+ "(see also <a href='http://x3dom.org/x3dom/doc/help/composedShader.html'>" +
+ "http://x3dom.org/x3dom/doc/help/composedShader.html</a>)\n" +
+ " attribute vec3 position;\n" +
+ " attribute vec3 normal;\n" +
+ " attribute vec2 texcoord;\n" +
+ " attribute vec3 color;\n" +
+ " uniform mat4 modelViewProjectionMatrix;\n" +
+ " uniform mat4 modelViewMatrix;\n" +
+ " uniform mat4 normalMatrix;\n" +
+ " uniform mat4 viewMatrix;\n" +
+ " uniform sampler2D tex;\n");
+ x3dom.nodeTypes.ComposedShader.ShaderInfoMsgShown = true;
+ }
+
+ },
+ {
+ nodeChanged: function()
+ {
+ var i, n = this._cf.parts.nodes.length;
+
+ for (i=0; i<n; i++)
+ {
+ if (this._cf.parts.nodes[i]._vf.type.toLowerCase() == 'vertex') {
+ this._vertex = this._cf.parts.nodes[i];
+ this._id = this._cf.parts.nodes[i]._id;
+ }
+ else if (this._cf.parts.nodes[i]._vf.type.toLowerCase() == 'fragment') {
+ this._fragment = this._cf.parts.nodes[i];
+ this._id += " - " + this._cf.parts.nodes[i]._id;
+ }
+ }
+
+ var ctx = {};
+ n = this._cf.fields.nodes.length;
+
+ for (i=0; i<n; i++)
+ {
+ var fieldName = this._cf.fields.nodes[i]._vf.name;
+ ctx.xmlNode = this._cf.fields.nodes[i]._xmlNode;
+
+ var needNode = false;
+
+ if (ctx.xmlNode === undefined || ctx.xmlNode === null) {
+ ctx.xmlNode = document.createElement("field");
+ needNode = true;
+ }
+
+ ctx.xmlNode.setAttribute(fieldName, this._cf.fields.nodes[i]._vf.value);
+
+ var funcName = "this.addField_" + this._cf.fields.nodes[i]._vf.type + "(ctx, name);";
+ var func = new Function('ctx', 'name', funcName);
+
+ func.call(this, ctx, fieldName);
+
+ if (needNode) {
+ ctx.xmlNode = null; // cleanup
+ }
+ }
+
+ Array.forEach(this._parentNodes, function (app) {
+ Array.forEach(app._parentNodes, function (shape) {
+ //shape.setAppDirty();
+ if (shape._cleanupGLObjects)
+ shape._cleanupGLObjects();
+ shape.setAllDirty();
+ });
+ });
+ },
+
+ fieldChanged: function(fieldName)
+ {
+ var i, n = this._cf.fields.nodes.length;
+
+ for (i=0; i<n; i++)
+ {
+ var field = this._cf.fields.nodes[i]._vf.name;
+
+ if (field === fieldName)
+ {
+ var msg = this._cf.fields.nodes[i]._vf.value;
+
+ try {
+ this._vf[field].setValueByStr(msg);
+ }
+ catch (exc1) {
+ try {
+ switch ((typeof(this._vf[field])).toString()) {
+ case "number":
+ this._vf[field] = +msg;
+ break;
+ case "boolean":
+ this._vf[field] = (msg.toLowerCase() === "true");
+ break;
+ case "string":
+ this._vf[field] = msg;
+ break;
+ }
+ }
+ catch (exc2) {
+ x3dom.debug.logError("setValueByStr() NYI for " + typeof(this._vf[field]));
+ }
+ }
+
+ break;
+ }
+ }
+
+ if (field === 'url')
+ {
+ Array.forEach(this._parentNodes, function (app) {
+ Array.forEach(app._parentNodes, function (shape) {
+ shape._dirty.shader = true;
+ });
+ });
+ }
+ },
+
+ parentAdded: function(parent)
+ {
+ //Array.forEach(this._parentNodes, function (app) {
+ // app.nodeChanged();
+ //});
+ parent.nodeChanged();
+ }
+ }
+ )
+);
+
+x3dom.nodeTypes.ComposedShader.ShaderInfoMsgShown = false;
+/** @namespace x3dom.nodeTypes */
+/*
+ * X3DOM JavaScript Library
+ * http://www.x3dom.org
+ *
+ * (C)2009 Fraunhofer IGD, Darmstadt, Germany
+ * Dual licensed under the MIT and GPL
+ */
+
+/* ### ShaderPart ### */
+x3dom.registerNodeType(
+ "ShaderPart",
+ "Shaders",
+ defineClass(x3dom.nodeTypes.X3DNode,
+
+ /**
+ * Constructor for ShaderPart
+ * @constructs x3dom.nodeTypes.ShaderPart
+ * @x3d 3.3
+ * @component Shaders
+ * @status full
+ * @extends x3dom.nodeTypes.X3DNode
+ * @param {Object} [ctx=null] - context object, containing initial settings like namespace
+ * @classdesc The ShaderPart node defines the source for a single object to be used by a ComposedShader node.
+ * The source is not required to be a complete shader for all of the vertex/fragment processing.
+ */
+ function (ctx) {
+ x3dom.nodeTypes.ShaderPart.superClass.call(this, ctx);
+
+
+ /**
+ * The shader source is read from the URL specified by the url field. When the url field contains no values
+ * ([]), this object instance is ignored.
+ * @var {x3dom.fields.MFString} url
+ * @memberof x3dom.nodeTypes.ShaderPart
+ * @initvalue []
+ * @field x3d
+ * @instance
+ */
+ this.addField_MFString(ctx, 'url', []);
+
+ /**
+ * The type field indicates whether this object shall be compiled as a vertex shader, fragment shader, or
+ * other future-defined shader type.
+ * @var {x3dom.fields.SFString} type
+ * @memberof x3dom.nodeTypes.ShaderPart
+ * @initvalue "VERTEX"
+ * @field x3d
+ * @instance
+ */
+ this.addField_SFString(ctx, 'type', "VERTEX");
+
+ this._id = (ctx && ctx.xmlNode && ctx.xmlNode.id != "") ?
+ ctx.xmlNode.id : ++x3dom.nodeTypes.Shape.shaderPartID;
+
+ x3dom.debug.assert(this._vf.type.toLowerCase() == 'vertex' ||
+ this._vf.type.toLowerCase() == 'fragment',
+ "Unknown shader part type!");
+ },
+ {
+ nodeChanged: function()
+ {
+ var ctx = {};
+ ctx.xmlNode = this._xmlNode;
+
+ if (ctx.xmlNode !== undefined && ctx.xmlNode !== null)
+ {
+ var that = this;
+
+ if (that._vf.url.length && that._vf.url[0].indexOf('\n') == -1)
+ {
+ var xhr = new XMLHttpRequest();
+ xhr.open("GET", that._nameSpace.getURL(that._vf.url[0]), false);
+ xhr.onload = function() {
+ that._vf.url = new x3dom.fields.MFString( [] );
+ that._vf.url.push(xhr.response);
+ };
+ xhr.onerror = function() {
+ x3dom.debug.logError("Could not load file '" + that._vf.url[0] + "'.");
+ };
+ xhr.send(null);
+ }
+ else
+ {
+ if (that._vf.url.length) {
+ that._vf.url = new x3dom.fields.MFString( [] );
+ }
+ try {
+ that._vf.url.push(ctx.xmlNode.childNodes[1].nodeValue);
+ ctx.xmlNode.removeChild(ctx.xmlNode.childNodes[1]);
+ }
+ catch(e) {
+ Array.forEach( ctx.xmlNode.childNodes, function (childDomNode) {
+ if (childDomNode.nodeType === 3) {
+ that._vf.url.push(childDomNode.nodeValue);
+ }
+ else if (childDomNode.nodeType === 4) {
+ that._vf.url.push(childDomNode.data);
+ }
+ childDomNode.parentNode.removeChild(childDomNode);
+ } );
+ }
+ }
+ }
+ // else hope that url field was already set somehow
+
+ Array.forEach(this._parentNodes, function (shader) {
+ shader.nodeChanged();
+ });
+ },
+
+ fieldChanged: function(fieldName)
+ {
+ if (fieldName === "url") {
+ Array.forEach(this._parentNodes, function (shader) {
+ shader.fieldChanged("url");
+ });
+ }
+ },
+
+ parentAdded: function(parent)
+ {
+ //Array.forEach(this._parentNodes, function (shader) {
+ // shader.nodeChanged();
+ //});
+ parent.nodeChanged();
+ }
+ }
+ )
+);
+
+/** @namespace x3dom.nodeTypes */
+/*
+ * X3DOM JavaScript Library
+ * http://www.x3dom.org
+ *
+ * (C)2009 Fraunhofer IGD, Darmstadt, Germany
+ * Dual licensed under the MIT and GPL
+ */
+
+/* ### X3DVertexAttributeNode ### */
+x3dom.registerNodeType(
+ "X3DVertexAttributeNode",
+ "Shaders",
+ defineClass(x3dom.nodeTypes.X3DGeometricPropertyNode,
+
+ /**
+ * Constructor for X3DVertexAttributeNode
+ * @constructs x3dom.nodeTypes.X3DVertexAttributeNode
+ * @x3d 3.3
+ * @component Shaders
+ * @status full
+ * @extends x3dom.nodeTypes.X3DGeometricPropertyNode
+ * @param {Object} [ctx=null] - context object, containing initial settings like namespace
+ * @classdesc This abstract node type is the base type for all node types that specify per-vertex attribute
+ * information to the shader.
+ */
+ function (ctx) {
+ x3dom.nodeTypes.X3DVertexAttributeNode.superClass.call(this, ctx);
+
+
+ /**
+ * The name field describes a name that is mapped to the shading language-specific name for describing
+ * per-vertex data. The appropriate shader language annex contains language-specific binding information.
+ * @var {x3dom.fields.SFString} name
+ * @memberof x3dom.nodeTypes.X3DVertexAttributeNode
+ * @initvalue ""
+ * @field x3d
+ * @instance
+ */
+ this.addField_SFString(ctx, 'name', "");
+
+ }
+ )
+);
+/** @namespace x3dom.nodeTypes */
+/*
+ * X3DOM JavaScript Library
+ * http://www.x3dom.org
+ *
+ * (C)2009 Fraunhofer IGD, Darmstadt, Germany
+ * Dual licensed under the MIT and GPL
+ */
+
+/* ### FloatVertexAttribute ### */
+x3dom.registerNodeType(
+ "FloatVertexAttribute",
+ "Shaders",
+ defineClass(x3dom.nodeTypes.X3DVertexAttributeNode,
+
+ /**
+ * Constructor for FloatVertexAttribute
+ * @constructs x3dom.nodeTypes.FloatVertexAttribute
+ * @x3d 3.3
+ * @component Shaders
+ * @status experimental
+ * @extends x3dom.nodeTypes.X3DVertexAttributeNode
+ * @param {Object} [ctx=null] - context object, containing initial settings like namespace
+ * @classdesc The FloatVertexAttribute node defines a set of per-vertex single-precision floating point
+ * attributes.
+ */
+ function (ctx) {
+ x3dom.nodeTypes.FloatVertexAttribute.superClass.call(this, ctx);
+
+
+ /**
+ * The numComponents field specifies how many consecutive floating point values should be grouped together
+ * per vertex. The length of the value field shall be a multiple of numComponents.
+ * @var {x3dom.fields.SFInt32} numComponents
+ * @memberof x3dom.nodeTypes.FloatVertexAttribute
+ * @initvalue 4
+ * @range [1..4]
+ * @field x3d
+ * @instance
+ */
+ this.addField_SFInt32(ctx, 'numComponents', 4);
+
+ /**
+ * The value field specifies an arbitrary collection of floating point values that will be passed to the
+ * shader as per-vertex information. The specific type mapping to the individual shading language data
+ * types is in the appropriate language-specific annex.
+ * @var {x3dom.fields.MFFloat} value
+ * @memberof x3dom.nodeTypes.FloatVertexAttribute
+ * @initvalue []
+ * @range (-inf, inf)
+ * @field x3d
+ * @instance
+ */
+ this.addField_MFFloat(ctx, 'value', []);
+
+ }
+ )
+);
+/** @namespace x3dom.nodeTypes */
+/*
+ * X3DOM JavaScript Library
+ * http://www.x3dom.org
+ *
+ * (C)2009 Fraunhofer IGD, Darmstadt, Germany
+ * Dual licensed under the MIT and GPL
+ */
+
+/* ### X3DSpatialGeometryNode ### */
+x3dom.registerNodeType(
+ "X3DSpatialGeometryNode",
+ "Geometry3D",
+ defineClass(x3dom.nodeTypes.X3DGeometryNode,
+
+ /**
+ * Constructor for X3DSpatialGeometryNode
+ * @constructs x3dom.nodeTypes.X3DSpatialGeometryNode
+ * @x3d x.x
+ * @component Geometry3D
+ * @status experimental
+ * @extends x3dom.nodeTypes.X3DGeometryNode
+ * @param {Object} [ctx=null] - context object, containing initial settings like namespace
+ * @classdesc This is the abstract node for spatial geometry nodes.
+ */
+ function (ctx) {
+ x3dom.nodeTypes.X3DSpatialGeometryNode.superClass.call(this, ctx);
+
+ }
+ )
+);
+/** @namespace x3dom.nodeTypes */
+/*
+ * X3DOM JavaScript Library
+ * http://www.x3dom.org
+ *
+ * (C)2009 Fraunhofer IGD, Darmstadt, Germany
+ * Dual licensed under the MIT and GPL
+ */
+
+/* ### Plane ### */
+x3dom.registerNodeType(
+ "Plane",
+ "Geometry3D",
+ defineClass(x3dom.nodeTypes.X3DSpatialGeometryNode,
+
+ /**
+ * Constructor for Plane
+ * @constructs x3dom.nodeTypes.Plane
+ * @x3d x.x
+ * @component Geometry3D
+ * @extends x3dom.nodeTypes.X3DSpatialGeometryNode
+ * @param {Object} [ctx=null] - context object, containing initial settings like namespace
+ * @class The plane node describes a plane shape that extents in x and y direction.
+ */
+ function (ctx) {
+ x3dom.nodeTypes.Plane.superClass.call(this, ctx);
+
+
+ /**
+ * The edge lengths of the plane.
+ * @var {x3dom.fields.SFVec2f} size
+ * @memberof x3dom.nodeTypes.Plane
+ * @initvalue 2,2
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFVec2f(ctx, 'size', 2, 2);
+
+ /**
+ * Defines the number of single elements that are generated to represent the plane.
+ * @var {x3dom.fields.SFVec2f} subdivision
+ * @memberof x3dom.nodeTypes.Plane
+ * @initvalue 1,1
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFVec2f(ctx, 'subdivision', 1, 1);
+
+ /**
+ * Defines the center point in the local coordinate system.
+ * @var {x3dom.fields.SFVec3f} center
+ * @memberof x3dom.nodeTypes.Plane
+ * @initvalue 0,0,0
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFVec3f(ctx, 'center', 0, 0, 0);
+
+ /**
+ * Specifies the primitive type that is used to build the plane.
+ * @var {x3dom.fields.MFString} primType
+ * @memberof x3dom.nodeTypes.Plane
+ * @initvalue ['TRIANGLES']
+ * @field x3dom
+ * @instance
+ */
+ this.addField_MFString(ctx, 'primType', ['TRIANGLES']);
+
+ // this way currently an initialize only field
+ if (this._vf.primType.length)
+ this._mesh._primType = this._vf.primType[0];
+
+ var sx = this._vf.size.x, sy = this._vf.size.y;
+ var subx = this._vf.subdivision.x, suby = this._vf.subdivision.y;
+
+ var geoCacheID = 'Plane_' + sx + '-' + sy + '-' + subx + '-' + suby + '-' +
+ this._vf.center.x + '-' + this._vf.center.y + '-' + this._vf.center.z;
+
+ // Attention: DynamicLOD node internally creates Plane nodes, but MUST NOT
+ // use geoCache, therefore only use cache if "ctx" is defined!
+ // TODO: move mesh generation of all primitives to nodeChanged()
+ if (ctx && this._vf.useGeoCache && x3dom.geoCache[geoCacheID] !== undefined) {
+ //x3dom.debug.logInfo("Using Plane from Cache");
+ this._mesh = x3dom.geoCache[geoCacheID];
+ }
+ else {
+ var x = 0, y = 0;
+ var xstep = sx / subx;
+ var ystep = sy / suby;
+
+ sx /= 2; sy /= 2;
+
+ for (y = 0; y <= suby; y++) {
+ for (x = 0; x <= subx; x++) {
+ this._mesh._positions[0].push(this._vf.center.x + x * xstep - sx);
+ this._mesh._positions[0].push(this._vf.center.y + y * ystep - sy);
+ this._mesh._positions[0].push(this._vf.center.z);
+ this._mesh._normals[0].push(0);
+ this._mesh._normals[0].push(0);
+ this._mesh._normals[0].push(1);
+ this._mesh._texCoords[0].push(x / subx);
+ this._mesh._texCoords[0].push(y / suby);
+ }
+ }
+
+ for (y = 1; y <= suby; y++) {
+ for (x = 0; x < subx; x++) {
+ this._mesh._indices[0].push((y - 1) * (subx + 1) + x);
+ this._mesh._indices[0].push((y - 1) * (subx + 1) + x + 1);
+ this._mesh._indices[0].push(y * (subx + 1) + x);
+
+ this._mesh._indices[0].push(y * (subx + 1) + x);
+ this._mesh._indices[0].push((y - 1) * (subx + 1) + x + 1);
+ this._mesh._indices[0].push(y * (subx + 1) + x + 1);
+ }
+ }
+
+ this._mesh._invalidate = true;
+ this._mesh._numFaces = this._mesh._indices[0].length / 3;
+ this._mesh._numCoords = this._mesh._positions[0].length / 3;
+
+ x3dom.geoCache[geoCacheID] = this._mesh;
+ }
+
+ },
+ {
+ fieldChanged: function (fieldName) {
+ if (fieldName == "size" || fieldName == "center") {
+ this._mesh._positions[0] = [];
+
+ var sx = this._vf.size.x, sy = this._vf.size.y;
+ var subx = this._vf.subdivision.x, suby = this._vf.subdivision.y;
+ var x = 0, y = 0;
+ var xstep = sx / subx;
+ var ystep = sy / suby;
+
+ sx /= 2; sy /= 2;
+
+ for (y = 0; y <= suby; y++) {
+ for (x = 0; x <= subx; x++) {
+ this._mesh._positions[0].push(this._vf.center.x + x * xstep - sx);
+ this._mesh._positions[0].push(this._vf.center.y + y * ystep - sy);
+ this._mesh._positions[0].push(this._vf.center.z);
+ }
+ }
+
+ this.invalidateVolume();
+ this._mesh._numCoords = this._mesh._positions[0].length / 3;
+
+ Array.forEach(this._parentNodes, function (node) {
+ node._dirty.positions = true;
+ node.invalidateVolume();
+ });
+ }
+ else if (fieldName == "subdivision") {
+ this._mesh._positions[0] = [];
+ this._mesh._indices[0] = [];
+ this._mesh._normals[0] = [];
+ this._mesh._texCoords[0] = [];
+
+ var sx = this._vf.size.x, sy = this._vf.size.y;
+ var subx = this._vf.subdivision.x, suby = this._vf.subdivision.y;
+
+ var x = 0, y = 0;
+ var xstep = sx / subx;
+ var ystep = sy / suby;
+
+ sx /= 2; sy /= 2;
+
+ for (y = 0; y <= suby; y++) {
+ for (x = 0; x <= subx; x++) {
+ this._mesh._positions[0].push(this._vf.center.x + x * xstep - sx);
+ this._mesh._positions[0].push(this._vf.center.y + y * ystep - sy);
+ this._mesh._positions[0].push(this._vf.center.z);
+ this._mesh._normals[0].push(0);
+ this._mesh._normals[0].push(0);
+ this._mesh._normals[0].push(1);
+ this._mesh._texCoords[0].push(x / subx);
+ this._mesh._texCoords[0].push(y / suby);
+ }
+ }
+
+ for (y = 1; y <= suby; y++) {
+ for (x = 0; x < subx; x++) {
+ this._mesh._indices[0].push((y - 1) * (subx + 1) + x);
+ this._mesh._indices[0].push((y - 1) * (subx + 1) + x + 1);
+ this._mesh._indices[0].push(y * (subx + 1) + x);
+
+ this._mesh._indices[0].push(y * (subx + 1) + x);
+ this._mesh._indices[0].push((y - 1) * (subx + 1) + x + 1);
+ this._mesh._indices[0].push(y * (subx + 1) + x + 1);
+ }
+ }
+
+ this.invalidateVolume();
+ this._mesh._numFaces = this._mesh._indices[0].length / 3;
+ this._mesh._numCoords = this._mesh._positions[0].length / 3;
+
+ Array.forEach(this._parentNodes, function (node) {
+ node.setAllDirty();
+ node.invalidateVolume();
+ });
+ }
+ }
+ }
+ )
+);
+/** @namespace x3dom.nodeTypes */
+/*
+ * X3DOM JavaScript Library
+ * http://www.x3dom.org
+ *
+ * (C)2009 Fraunhofer IGD, Darmstadt, Germany
+ * Dual licensed under the MIT and GPL
+ */
+
+/* ### Box ### */
+x3dom.registerNodeType(
+ "Box",
+ "Geometry3D",
+ defineClass(x3dom.nodeTypes.X3DSpatialGeometryNode,
+
+ /**
+ * Constructor for Box
+ * @constructs x3dom.nodeTypes.Box
+ * @x3d 3.3
+ * @component Geometry3D
+ * @status full
+ * @extends x3dom.nodeTypes.X3DSpatialGeometryNode
+ * @param {Object} [ctx=null] - context object, containing initial settings like namespace
+ * @classdesc The Box node specifies a rectangular parallelepiped box centred at (0, 0, 0) in the local coordinate system and aligned with the local coordinate axes. By default, the box measures 2 units in each dimension, from -1 to +1.
+ */
+ function (ctx) {
+ x3dom.nodeTypes.Box.superClass.call(this, ctx);
+
+
+ /**
+ * The size field specifies the extents of the box along the X-, Y-, and Z-axes respectively and each component value shall be greater than zero.
+ * @var {x3dom.fields.SFVec3f} size
+ * @range [0, inf]
+ * @memberof x3dom.nodeTypes.Box
+ * @initvalue 2,2,2
+ * @field x3d
+ * @instance
+ */
+ this.addField_SFVec3f(ctx, 'size', 2, 2, 2);
+
+ /**
+ * Specifies whether helper colors should be used, which will color each vertex with a different color. This will overwrite the color of the corresponding appearance node.
+ * @var {x3dom.fields.SFBool} hasHelperColors
+ * @memberof x3dom.nodeTypes.Box
+ * @initvalue false
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFBool(ctx, 'hasHelperColors', false);
+
+ var sx = this._vf.size.x,
+ sy = this._vf.size.y,
+ sz = this._vf.size.z;
+
+ var geoCacheID = 'Box_'+sx+'-'+sy+'-'+sz;
+
+ if( this._vf.useGeoCache && x3dom.geoCache[geoCacheID] !== undefined )
+ {
+ //x3dom.debug.logInfo("Using Box from Cache");
+ this._mesh = x3dom.geoCache[geoCacheID];
+ }
+ else
+ {
+ sx /= 2; sy /= 2; sz /= 2;
+
+ this._mesh._positions[0] = [
+ -sx,-sy,-sz, -sx, sy,-sz, sx, sy,-sz, sx,-sy,-sz, //hinten 0,0,-1
+ -sx,-sy, sz, -sx, sy, sz, sx, sy, sz, sx,-sy, sz, //vorne 0,0,1
+ -sx,-sy,-sz, -sx,-sy, sz, -sx, sy, sz, -sx, sy,-sz, //links -1,0,0
+ sx,-sy,-sz, sx,-sy, sz, sx, sy, sz, sx, sy,-sz, //rechts 1,0,0
+ -sx, sy,-sz, -sx, sy, sz, sx, sy, sz, sx, sy,-sz, //oben 0,1,0
+ -sx,-sy,-sz, -sx,-sy, sz, sx,-sy, sz, sx,-sy,-sz //unten 0,-1,0
+ ];
+ this._mesh._normals[0] = [
+ 0,0,-1, 0,0,-1, 0,0,-1, 0,0,-1,
+ 0,0,1, 0,0,1, 0,0,1, 0,0,1,
+ -1,0,0, -1,0,0, -1,0,0, -1,0,0,
+ 1,0,0, 1,0,0, 1,0,0, 1,0,0,
+ 0,1,0, 0,1,0, 0,1,0, 0,1,0,
+ 0,-1,0, 0,-1,0, 0,-1,0, 0,-1,0
+ ];
+ this._mesh._texCoords[0] = [
+ 1,0, 1,1, 0,1, 0,0,
+ 0,0, 0,1, 1,1, 1,0,
+ 0,0, 1,0, 1,1, 0,1,
+ 1,0, 0,0, 0,1, 1,1,
+ 0,1, 0,0, 1,0, 1,1,
+ 0,0, 0,1, 1,1, 1,0
+ ];
+ if (this._vf.hasHelperColors) {
+ this._mesh._colors[0] = [
+ 0, 0, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0,
+ 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1,
+ 0, 0, 0, 0, 0, 1, 0, 1, 1, 0, 1, 0,
+ 1, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0,
+ 0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0,
+ 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0
+ ];
+ }
+ this._mesh._indices[0] = [
+ 0,1,2, 2,3,0,
+ 4,7,5, 5,7,6,
+ 8,9,10, 10,11,8,
+ 12,14,13, 14,12,15,
+ 16,17,18, 18,19,16,
+ 20,22,21, 22,20,23
+ ];
+ this._mesh._invalidate = true;
+ this._mesh._numFaces = 12;
+ this._mesh._numCoords = 24;
+
+ x3dom.geoCache[geoCacheID] = this._mesh;
+ }
+
+ },
+ {
+ fieldChanged: function (fieldName)
+ {
+ if (fieldName === "size") {
+ var sx = this._vf.size.x / 2,
+ sy = this._vf.size.y / 2,
+ sz = this._vf.size.z / 2;
+
+ this._mesh._positions[0] = [
+ -sx,-sy,-sz, -sx, sy,-sz, sx, sy,-sz, sx,-sy,-sz, //back 0,0,-1
+ -sx,-sy, sz, -sx, sy, sz, sx, sy, sz, sx,-sy, sz, //front 0,0,1
+ -sx,-sy,-sz, -sx,-sy, sz, -sx, sy, sz, -sx, sy,-sz, //left -1,0,0
+ sx,-sy,-sz, sx,-sy, sz, sx, sy, sz, sx, sy,-sz, //right 1,0,0
+ -sx, sy,-sz, -sx, sy, sz, sx, sy, sz, sx, sy,-sz, //top 0,1,0
+ -sx,-sy,-sz, -sx,-sy, sz, sx,-sy, sz, sx,-sy,-sz //bottom 0,-1,0
+ ];
+
+ this.invalidateVolume();
+
+ Array.forEach(this._parentNodes, function (node) {
+ node._dirty.positions = true;
+ node.invalidateVolume();
+ });
+ }
+ else if (fieldName === "hasHelperColors") {
+ if (this._vf.hasHelperColors) {
+ this._mesh._colors[0] = [
+ 0, 0, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0,
+ 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1,
+ 0, 0, 0, 0, 0, 1, 0, 1, 1, 0, 1, 0,
+ 1, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0,
+ 0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0,
+ 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0
+ ];
+ }
+ else {
+ this._mesh._colors[0] = [];
+ }
+
+ Array.forEach(this._parentNodes, function (node) {
+ node._dirty.colors = true;
+ });
+ }
+ }
+ }
+ )
+);
+/** @namespace x3dom.nodeTypes */
+/*
+ * X3DOM JavaScript Library
+ * http://www.x3dom.org
+ *
+ * (C)2009 Fraunhofer IGD, Darmstadt, Germany
+ * Dual licensed under the MIT and GPL
+ */
+
+/* ### Sphere ### */
+x3dom.registerNodeType(
+ "Sphere",
+ "Geometry3D",
+ defineClass(x3dom.nodeTypes.X3DSpatialGeometryNode,
+
+ /**
+ * Constructor for Sphere
+ * @constructs x3dom.nodeTypes.Sphere
+ * @x3d 3.3
+ * @component Geometry3D
+ * @status experimental
+ * @extends x3dom.nodeTypes.X3DSpatialGeometryNode
+ * @param {Object} [ctx=null] - context object, containing initial settings like namespace
+ * @classdesc The Sphere node specifies a sphere centred at (0, 0, 0) in the local coordinate system.
+ */
+ function (ctx) {
+ x3dom.nodeTypes.Sphere.superClass.call(this, ctx);
+
+ // sky box background creates sphere with r = 10000
+
+ /**
+ * The radius field specifies the radius of the sphere.
+ * @var {x3dom.fields.SFFloat} radius
+ * @range [0, inf]
+ * @memberof x3dom.nodeTypes.Sphere
+ * @initvalue ctx?1:10000
+ * @field x3d
+ * @instance
+ */
+ this.addField_SFFloat(ctx, 'radius', ctx ? 1 : 10000);
+
+ /**
+ * Specifies the number of faces that are generated to approximate the surface of the sphere.
+ * @var {x3dom.fields.SFVec2f} subdivision
+ * @memberof x3dom.nodeTypes.Sphere
+ * @initvalue 24,24
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFVec2f(ctx, 'subdivision', 24, 24);
+
+ var qfactor = 1.0;
+ var r = this._vf.radius;
+ var subx = this._vf.subdivision.x, suby = this._vf.subdivision.y;
+
+ var geoCacheID = 'Sphere_' + r + '-' + subx + '-' + suby;
+
+ if (this._vf.useGeoCache && x3dom.geoCache[geoCacheID] !== undefined) {
+ //x3dom.debug.logInfo("Using Sphere from Cache");
+ this._mesh = x3dom.geoCache[geoCacheID];
+ }
+ else {
+ if(ctx) {
+ qfactor = ctx.doc.properties.getProperty("PrimitiveQuality", "Medium");
+ }
+ if (!x3dom.Utils.isNumber(qfactor)) {
+ switch (qfactor.toLowerCase()) {
+ case "low":
+ qfactor = 0.3;
+ break;
+ case "medium":
+ qfactor = 0.5;
+ break;
+ case "high":
+ qfactor = 1.0;
+ break;
+ }
+ } else {
+ qfactor = parseFloat(qfactor);
+ }
+
+ this._quality = qfactor;
+
+ var latNumber, longNumber;
+ var latitudeBands = Math.floor(subx * qfactor);
+ var longitudeBands = Math.floor(suby * qfactor);
+
+ var theta, sinTheta, cosTheta;
+ var phi, sinPhi, cosPhi;
+ var x, y, z, u, v;
+
+ for (latNumber = 0; latNumber <= latitudeBands; latNumber++) {
+ theta = (latNumber * Math.PI) / latitudeBands;
+ sinTheta = Math.sin(theta);
+ cosTheta = Math.cos(theta);
+
+ for (longNumber = 0; longNumber <= longitudeBands; longNumber++) {
+ phi = (longNumber * 2.0 * Math.PI) / longitudeBands;
+ sinPhi = Math.sin(phi);
+ cosPhi = Math.cos(phi);
+
+ x = -cosPhi * sinTheta;
+ y = -cosTheta;
+ z = -sinPhi * sinTheta;
+
+ u = 0.25 - (longNumber / longitudeBands);
+ v = latNumber / latitudeBands;
+
+ this._mesh._positions[0].push(r * x);
+ this._mesh._positions[0].push(r * y);
+ this._mesh._positions[0].push(r * z);
+ this._mesh._normals[0].push(x);
+ this._mesh._normals[0].push(y);
+ this._mesh._normals[0].push(z);
+ this._mesh._texCoords[0].push(u);
+ this._mesh._texCoords[0].push(v);
+ }
+ }
+
+ var first, second;
+
+ for (latNumber = 0; latNumber < latitudeBands; latNumber++) {
+ for (longNumber = 0; longNumber < longitudeBands; longNumber++) {
+ first = (latNumber * (longitudeBands + 1)) + longNumber;
+ second = first + longitudeBands + 1;
+
+ this._mesh._indices[0].push(first);
+ this._mesh._indices[0].push(second);
+ this._mesh._indices[0].push(first + 1);
+
+ this._mesh._indices[0].push(second);
+ this._mesh._indices[0].push(second + 1);
+ this._mesh._indices[0].push(first + 1);
+ }
+ }
+
+ this._mesh._invalidate = true;
+ this._mesh._numFaces = this._mesh._indices[0].length / 3;
+ this._mesh._numCoords = this._mesh._positions[0].length / 3;
+
+ x3dom.geoCache[geoCacheID] = this._mesh;
+ }
+
+ },
+ {
+ fieldChanged: function(fieldName) {
+ if (fieldName === "radius") {
+ this._mesh._positions[0] = [];
+ var r = this._vf.radius;
+ var subx = this._vf.subdivision.x, suby = this._vf.subdivision.y;
+ var qfactor = this._quality;
+
+ var latNumber, longNumber;
+ var latitudeBands = Math.floor(subx * qfactor);
+ var longitudeBands = Math.floor(suby * qfactor);
+
+ var theta, sinTheta, cosTheta;
+ var phi, sinPhi, cosPhi;
+ var x, y, z;
+
+ for (latNumber = 0; latNumber <= latitudeBands; latNumber++) {
+ theta = (latNumber * Math.PI) / latitudeBands;
+ sinTheta = Math.sin(theta);
+ cosTheta = Math.cos(theta);
+
+ for (longNumber = 0; longNumber <= longitudeBands; longNumber++) {
+ phi = (longNumber * 2.0 * Math.PI) / longitudeBands;
+ sinPhi = Math.sin(phi);
+ cosPhi = Math.cos(phi);
+
+ x = -cosPhi * sinTheta;
+ y = -cosTheta;
+ z = -sinPhi * sinTheta;
+
+ this._mesh._positions[0].push(r * x);
+ this._mesh._positions[0].push(r * y);
+ this._mesh._positions[0].push(r * z);
+ }
+ }
+
+ this.invalidateVolume();
+ this._mesh._numCoords = this._mesh._positions[0].length / 3;
+
+ Array.forEach(this._parentNodes, function (node) {
+ node._dirty.positions = true;
+ node.invalidateVolume();
+ });
+ }
+ else if (fieldName === "subdivision") {
+ this._mesh._positions[0] = [];
+ this._mesh._indices[0] =[];
+ this._mesh._normals[0] = [];
+ this._mesh._texCoords[0] =[];
+
+ var r = this._vf.radius;
+ var subx = this._vf.subdivision.x, suby = this._vf.subdivision.y;
+ var qfactor = this._quality;
+
+ var latNumber, longNumber;
+ var latitudeBands = Math.floor(subx * qfactor);
+ var longitudeBands = Math.floor(suby * qfactor);
+
+ var theta, sinTheta, cosTheta;
+ var phi, sinPhi, cosPhi;
+ var x, y, z, u, v;
+
+ for (latNumber = 0; latNumber <= latitudeBands; latNumber++) {
+ theta = (latNumber * Math.PI) / latitudeBands;
+ sinTheta = Math.sin(theta);
+ cosTheta = Math.cos(theta);
+
+ for (longNumber = 0; longNumber <= longitudeBands; longNumber++) {
+ phi = (longNumber * 2.0 * Math.PI) / longitudeBands;
+ sinPhi = Math.sin(phi);
+ cosPhi = Math.cos(phi);
+
+ x = -cosPhi * sinTheta;
+ y = -cosTheta;
+ z = -sinPhi * sinTheta;
+
+ u = 0.25 - (longNumber / longitudeBands);
+ v = latNumber / latitudeBands;
+
+ this._mesh._positions[0].push(r * x);
+ this._mesh._positions[0].push(r * y);
+ this._mesh._positions[0].push(r * z);
+ this._mesh._normals[0].push(x);
+ this._mesh._normals[0].push(y);
+ this._mesh._normals[0].push(z);
+ this._mesh._texCoords[0].push(u);
+ this._mesh._texCoords[0].push(v);
+ }
+ }
+
+ var first, second;
+
+ for (latNumber = 0; latNumber < latitudeBands; latNumber++) {
+ for (longNumber = 0; longNumber < longitudeBands; longNumber++) {
+ first = (latNumber * (longitudeBands + 1)) + longNumber;
+ second = first + longitudeBands + 1;
+
+ this._mesh._indices[0].push(first);
+ this._mesh._indices[0].push(second);
+ this._mesh._indices[0].push(first + 1);
+
+ this._mesh._indices[0].push(second);
+ this._mesh._indices[0].push(second + 1);
+ this._mesh._indices[0].push(first + 1);
+ }
+ }
+
+ this.invalidateVolume();
+ this._mesh._numFaces = this._mesh._indices[0].length / 3;
+ this._mesh._numCoords = this._mesh._positions[0].length / 3;
+
+ Array.forEach(this._parentNodes, function (node) {
+ node.setAllDirty();
+ node.invalidateVolume();
+ });
+ }
+ }
+ }
+ )
+);
+/** @namespace x3dom.nodeTypes */
+/*
+ * X3DOM JavaScript Library
+ * http://www.x3dom.org
+ *
+ * (C)2009 Fraunhofer IGD, Darmstadt, Germany
+ * Dual licensed under the MIT and GPL
+ */
+
+/* ### Torus ### */
+x3dom.registerNodeType(
+ "Torus",
+ "Geometry3D",
+ defineClass(x3dom.nodeTypes.X3DSpatialGeometryNode,
+
+ /**
+ * Constructor for Torus
+ * @constructs x3dom.nodeTypes.Torus
+ * @x3d x.x
+ * @component Geometry3D
+ * @status experimental
+ * @extends x3dom.nodeTypes.X3DSpatialGeometryNode
+ * @param {Object} [ctx=null] - context object, containing initial settings like namespace
+ * @classdesc The Torus node specifies a torus shape centred at (0, 0, 0) in the local coordinate system.
+ */
+ function (ctx) {
+ x3dom.nodeTypes.Torus.superClass.call(this, ctx);
+
+ var twoPi = 2.0 * Math.PI;
+
+
+ /**
+ * Specifies the inner radius of the torus.
+ * @var {x3dom.fields.SFFloat} innerRadius
+ * @memberof x3dom.nodeTypes.Torus
+ * @initvalue 0.5
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFFloat(ctx, 'innerRadius', 0.5);
+
+ /**
+ * Specifies the outer radius of the torus.
+ * @var {x3dom.fields.SFFloat} outerRadius
+ * @memberof x3dom.nodeTypes.Torus
+ * @initvalue 1.0
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFFloat(ctx, 'outerRadius', 1.0);
+
+ /**
+ * Specifies the size of the torus as an angle.
+ * @var {x3dom.fields.SFFloat} angle
+ * @range [0, 2*pi]
+ * @memberof x3dom.nodeTypes.Torus
+ * @initvalue twoPi
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFFloat(ctx, 'angle', twoPi);
+
+ /**
+ * Specifies whether the torus ends are closed with caps (when angle is smaller than a full circle).
+ * @var {x3dom.fields.SFBool} caps
+ * @memberof x3dom.nodeTypes.Torus
+ * @initvalue true
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFBool(ctx, 'caps', true);
+
+ /**
+ * Specifies the number of faces that are generated to approximate the torus.
+ * @var {x3dom.fields.SFVec2f} subdivision
+ * @memberof x3dom.nodeTypes.Torus
+ * @initvalue 24,24
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFVec2f(ctx, 'subdivision', 24, 24);
+
+ /**
+ * Use a different interpretation mode for the inside and outside radius.
+ * @var {x3dom.fields.SFBool} insideOutsideRadius
+ * @memberof x3dom.nodeTypes.Torus
+ * @initvalue false
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFBool(ctx, 'insideOutsideRadius', false);
+
+ // assure that angle in [0, 2 * PI]
+ if (this._vf.angle < 0)
+ this._vf.angle = 0;
+ else if (this._vf.angle > twoPi)
+ this._vf.angle = twoPi;
+
+ this._origCCW = this._vf.ccw;
+
+ var innerRadius = this._vf.innerRadius;
+ var outerRadius = this._vf.outerRadius;
+
+ if (this._vf.insideOutsideRadius == true)
+ {
+ if (innerRadius > outerRadius) {
+ var tmp = innerRadius;
+ innerRadius = outerRadius;
+ outerRadius = tmp;
+ }
+
+ var rad = (outerRadius - innerRadius) / 2;
+
+ outerRadius = innerRadius + rad;
+ innerRadius = rad;
+
+ // fix wrong face orientation in case of clockwise rotation
+ this._vf.ccw = !this._origCCW;
+ }
+
+ var rings = this._vf.subdivision.x, sides = this._vf.subdivision.y;
+ rings = Math.max(3, Math.round((this._vf.angle / twoPi) * rings));
+
+ // FIXME; check/update geoCache on field update (for ALL primitives)!
+ var geoCacheID = 'Torus_'+innerRadius+'_'+outerRadius+'_'+this._vf.angle+'_'+
+ this._vf.subdivision+'-'+this._vf.caps;
+
+ if( this._vf.useGeoCache && x3dom.geoCache[geoCacheID] !== undefined )
+ {
+ //x3dom.debug.logInfo("Using Torus from Cache");
+ this._mesh = x3dom.geoCache[geoCacheID];
+ }
+ else
+ {
+ var ringDelta = this._vf.angle / rings;
+ var sideDelta = twoPi / sides;
+ var a, b, theta, phi;
+ var cosTheta, sinTheta, cosPhi, sinPhi, dist;
+
+ for (a=0, theta=0; a <= rings; a++, theta+=ringDelta)
+ {
+ cosTheta = Math.cos(theta);
+ sinTheta = Math.sin(theta);
+
+ for (b=0, phi=0; b<=sides; b++, phi+=sideDelta)
+ {
+ cosPhi = Math.cos(phi);
+ sinPhi = Math.sin(phi);
+ dist = outerRadius + innerRadius * cosPhi;
+
+ if (this._vf.insideOutsideRadius) {
+ this._mesh._positions[0].push(cosTheta * dist, innerRadius * sinPhi, -sinTheta * dist);
+ this._mesh._normals[0].push(cosTheta * cosPhi, sinPhi, -sinTheta * cosPhi);
+ }
+ else {
+ this._mesh._positions[0].push(cosTheta * dist, -sinTheta * dist, innerRadius * sinPhi);
+ this._mesh._normals[0].push(cosTheta * cosPhi, -sinTheta * cosPhi, sinPhi);
+ }
+ this._mesh._texCoords[0].push(-a / rings, b / sides);
+ }
+ }
+
+ for (a=0; a<sides; a++)
+ {
+ for (b=0; b<rings; b++)
+ {
+ this._mesh._indices[0].push(b * (sides+1) + a);
+ this._mesh._indices[0].push(b * (sides+1) + a + 1);
+ this._mesh._indices[0].push((b + 1) * (sides+1) + a);
+
+ this._mesh._indices[0].push(b * (sides+1) + a + 1);
+ this._mesh._indices[0].push((b + 1) * (sides+1) + a + 1);
+ this._mesh._indices[0].push((b + 1) * (sides+1) + a);
+ }
+ }
+
+ if (this._vf.angle < twoPi && this._vf.caps == true)
+ {
+ // create first cap
+ var origPos = this._mesh._positions[0].length / 3;
+
+ if (this._vf.insideOutsideRadius) {
+ this._mesh._positions[0].push(outerRadius, 0, 0);
+ this._mesh._normals[0].push(0, 0, 1);
+ }
+ else {
+ this._mesh._positions[0].push(outerRadius, 0, 0);
+ this._mesh._normals[0].push(0, 1, 0);
+ }
+ this._mesh._texCoords[0].push(0.5, 0.5);
+
+ for (b=0, phi=0; b<=sides; b++, phi+=sideDelta)
+ {
+ cosPhi = Math.cos(phi);
+ sinPhi = Math.sin(phi);
+ dist = outerRadius + innerRadius * cosPhi;
+
+ if (this._vf.insideOutsideRadius) {
+ this._mesh._positions[0].push(dist, sinPhi * innerRadius, 0);
+ this._mesh._normals[0].push(0, 0, 1);
+ }
+ else {
+ this._mesh._positions[0].push(dist, 0, sinPhi * innerRadius);
+ this._mesh._normals[0].push(0, 1, 0);
+ }
+ this._mesh._texCoords[0].push((1 + cosPhi) * 0.5, (1 - sinPhi) * 0.5);
+
+ if (b > 0) {
+ this._mesh._indices[0].push(origPos);
+ this._mesh._indices[0].push(origPos + b);
+ this._mesh._indices[0].push(origPos + b - 1);
+ }
+ if (b == sides) {
+ this._mesh._indices[0].push(origPos);
+ this._mesh._indices[0].push(origPos + 1);
+ this._mesh._indices[0].push(origPos + b);
+ }
+ }
+
+ // second cap
+ cosTheta = Math.cos(this._vf.angle);
+ sinTheta = Math.sin(this._vf.angle);
+
+ origPos = this._mesh._positions[0].length / 3;
+ var nx = -sinTheta, ny = -cosTheta;
+
+ if (this._vf.insideOutsideRadius) {
+ this._mesh._positions[0].push(cosTheta * outerRadius, 0, -sinTheta * outerRadius);
+ this._mesh._normals[0].push(nx, 0, ny);
+ }
+ else {
+ this._mesh._positions[0].push(cosTheta * outerRadius, -sinTheta * outerRadius, 0);
+ this._mesh._normals[0].push(nx, ny, 0);
+ }
+ this._mesh._texCoords[0].push(0.5, 0.5);
+
+ for (b=0, phi=0; b<=sides; b++, phi+=sideDelta)
+ {
+ cosPhi = Math.cos(phi);
+ sinPhi = Math.sin(phi);
+ dist = outerRadius + innerRadius * cosPhi;
+
+ if (this._vf.insideOutsideRadius) {
+ this._mesh._positions[0].push(cosTheta * dist, sinPhi * innerRadius, -sinTheta * dist);
+ this._mesh._normals[0].push(nx, 0, ny);
+ }
+ else {
+ this._mesh._positions[0].push(cosTheta * dist, -sinTheta * dist, sinPhi * innerRadius);
+ this._mesh._normals[0].push(nx, ny, 0);
+ }
+ this._mesh._texCoords[0].push(1 - (1 + cosPhi) * 0.5, (1 - sinPhi) * 0.5);
+
+ if (b > 0) {
+ this._mesh._indices[0].push(origPos);
+ this._mesh._indices[0].push(origPos + b - 1);
+ this._mesh._indices[0].push(origPos + b);
+ }
+ if (b == sides) {
+ this._mesh._indices[0].push(origPos);
+ this._mesh._indices[0].push(origPos + b);
+ this._mesh._indices[0].push(origPos + 1);
+ }
+ }
+ }
+
+ this._mesh._invalidate = true;
+ this._mesh._numFaces = this._mesh._indices[0].length / 3;
+ this._mesh._numCoords = this._mesh._positions[0].length / 3;
+
+ x3dom.geoCache[geoCacheID] = this._mesh;
+ }
+
+ },
+ {
+ fieldChanged: function(fieldName)
+ {
+ // TODO; invalidate geometry cache if necessary (to be fixed for all primitives)!
+ if (fieldName == "innerRadius" || fieldName == "outerRadius" ||
+ fieldName == "subdivision" || fieldName == "angle" ||
+ fieldName == "insideOutsideRadius" || fieldName == "caps")
+ {
+ // assure that angle in [0, 2 * PI]
+ var twoPi = 2.0 * Math.PI;
+
+ if (this._vf.angle < 0)
+ this._vf.angle = 0;
+ else if (this._vf.angle > twoPi)
+ this._vf.angle = twoPi;
+
+ var innerRadius = this._vf.innerRadius;
+ var outerRadius = this._vf.outerRadius;
+
+ if (this._vf.insideOutsideRadius == true)
+ {
+ if (innerRadius > outerRadius) {
+ var tmp = innerRadius;
+ innerRadius = outerRadius;
+ outerRadius = tmp;
+ }
+
+ var rad = (outerRadius - innerRadius) / 2;
+
+ outerRadius = innerRadius + rad;
+ innerRadius = rad;
+
+ this._vf.ccw = !this._origCCW;
+ }
+ else
+ this._vf.ccw = this._origCCW;
+
+ var rings = this._vf.subdivision.x, sides = this._vf.subdivision.y;
+ rings = Math.max(3, Math.round((this._vf.angle / twoPi) * rings));
+
+ var ringDelta = this._vf.angle / rings;
+ var sideDelta = twoPi / sides;
+ var a, b, theta, phi;
+ var cosTheta, sinTheta, cosPhi, sinPhi, dist;
+
+ this._mesh._positions[0] = [];
+ this._mesh._normals[0] = [];
+ this._mesh._texCoords[0] = [];
+ this._mesh._indices[0] = [];
+
+ for (a=0, theta=0; a <= rings; a++, theta+=ringDelta)
+ {
+ cosTheta = Math.cos(theta);
+ sinTheta = Math.sin(theta);
+
+ for (b=0, phi=0; b<=sides; b++, phi+=sideDelta)
+ {
+ cosPhi = Math.cos(phi);
+ sinPhi = Math.sin(phi);
+ dist = outerRadius + innerRadius * cosPhi;
+
+ if (this._vf.insideOutsideRadius) {
+ this._mesh._positions[0].push(cosTheta * dist, innerRadius * sinPhi, -sinTheta * dist);
+ this._mesh._normals[0].push(cosTheta * cosPhi, sinPhi, -sinTheta * cosPhi);
+ }
+ else {
+ this._mesh._positions[0].push(cosTheta * dist, -sinTheta * dist, innerRadius * sinPhi);
+ this._mesh._normals[0].push(cosTheta * cosPhi, -sinTheta * cosPhi, sinPhi);
+ }
+ this._mesh._texCoords[0].push(-a / rings, b / sides);
+ }
+ }
+
+ for (a=0; a<sides; a++)
+ {
+ for (b=0; b<rings; b++)
+ {
+ this._mesh._indices[0].push(b * (sides+1) + a);
+ this._mesh._indices[0].push(b * (sides+1) + a + 1);
+ this._mesh._indices[0].push((b + 1) * (sides+1) + a);
+
+ this._mesh._indices[0].push(b * (sides+1) + a + 1);
+ this._mesh._indices[0].push((b + 1) * (sides+1) + a + 1);
+ this._mesh._indices[0].push((b + 1) * (sides+1) + a);
+ }
+ }
+
+ if (this._vf.angle < twoPi && this._vf.caps == true)
+ {
+ // create first cap
+ var origPos = this._mesh._positions[0].length / 3;
+
+ if (this._vf.insideOutsideRadius) {
+ this._mesh._positions[0].push(outerRadius, 0, 0);
+ this._mesh._normals[0].push(0, 0, 1);
+ }
+ else {
+ this._mesh._positions[0].push(outerRadius, 0, 0);
+ this._mesh._normals[0].push(0, 1, 0);
+ }
+ this._mesh._texCoords[0].push(0.5, 0.5);
+
+ for (b=0, phi=0; b<=sides; b++, phi+=sideDelta)
+ {
+ cosPhi = Math.cos(phi);
+ sinPhi = Math.sin(phi);
+ dist = outerRadius + innerRadius * cosPhi;
+
+ if (this._vf.insideOutsideRadius) {
+ this._mesh._positions[0].push(dist, sinPhi * innerRadius, 0);
+ this._mesh._normals[0].push(0, 0, 1);
+ }
+ else {
+ this._mesh._positions[0].push(dist, 0, sinPhi * innerRadius);
+ this._mesh._normals[0].push(0, 1, 0);
+ }
+ this._mesh._texCoords[0].push((1 + cosPhi) * 0.5, (1 - sinPhi) * 0.5);
+
+ if (b > 0) {
+ this._mesh._indices[0].push(origPos);
+ this._mesh._indices[0].push(origPos + b);
+ this._mesh._indices[0].push(origPos + b - 1);
+ }
+ if (b == sides) {
+ this._mesh._indices[0].push(origPos);
+ this._mesh._indices[0].push(origPos + 1);
+ this._mesh._indices[0].push(origPos + b);
+ }
+ }
+
+ // second cap
+ cosTheta = Math.cos(this._vf.angle);
+ sinTheta = Math.sin(this._vf.angle);
+
+ origPos = this._mesh._positions[0].length / 3;
+ var nx = -sinTheta, ny = -cosTheta;
+
+ if (this._vf.insideOutsideRadius) {
+ this._mesh._positions[0].push(cosTheta * outerRadius, 0, -sinTheta * outerRadius);
+ this._mesh._normals[0].push(nx, 0, ny);
+ }
+ else {
+ this._mesh._positions[0].push(cosTheta * outerRadius, -sinTheta * outerRadius, 0);
+ this._mesh._normals[0].push(nx, ny, 0);
+ }
+ this._mesh._texCoords[0].push(0.5, 0.5);
+
+ for (b=0, phi=0; b<=sides; b++, phi+=sideDelta)
+ {
+ cosPhi = Math.cos(phi);
+ sinPhi = Math.sin(phi);
+ dist = outerRadius + innerRadius * cosPhi;
+
+ if (this._vf.insideOutsideRadius) {
+ this._mesh._positions[0].push(cosTheta * dist, sinPhi * innerRadius, -sinTheta * dist);
+ this._mesh._normals[0].push(nx, 0, ny);
+ }
+ else {
+ this._mesh._positions[0].push(cosTheta * dist, -sinTheta * dist, sinPhi * innerRadius);
+ this._mesh._normals[0].push(nx, ny, 0);
+ }
+ this._mesh._texCoords[0].push(1 - (1 + cosPhi) * 0.5, (1 - sinPhi) * 0.5);
+
+ if (b > 0) {
+ this._mesh._indices[0].push(origPos);
+ this._mesh._indices[0].push(origPos + b - 1);
+ this._mesh._indices[0].push(origPos + b);
+ }
+ if (b == sides) {
+ this._mesh._indices[0].push(origPos);
+ this._mesh._indices[0].push(origPos + b);
+ this._mesh._indices[0].push(origPos + 1);
+ }
+ }
+ }
+
+ this.invalidateVolume();
+ this._mesh._numFaces = this._mesh._indices[0].length / 3;
+ this._mesh._numCoords = this._mesh._positions[0].length / 3;
+
+ Array.forEach(this._parentNodes, function (node) {
+ node.setAllDirty();
+ node.invalidateVolume();
+ });
+ }
+ }
+ }
+ )
+);
+/** @namespace x3dom.nodeTypes */
+/*
+ * X3DOM JavaScript Library
+ * http://www.x3dom.org
+ *
+ * (C)2009 Fraunhofer IGD, Darmstadt, Germany
+ * Dual licensed under the MIT and GPL
+ */
+
+/* ### Cone ### */
+x3dom.registerNodeType(
+ "Cone",
+ "Geometry3D",
+ defineClass(x3dom.nodeTypes.X3DSpatialGeometryNode,
+
+ /**
+ * Constructor for Cone
+ * @constructs x3dom.nodeTypes.Cone
+ * @x3d 3.3
+ * @component Geometry3D
+ * @status full
+ * @extends x3dom.nodeTypes.X3DSpatialGeometryNode
+ * @param {Object} [ctx=null] - context object, containing initial settings like namespace
+ * @classdesc The Cone node specifies a cone which is centred in the local coordinate system and whose central axis is aligned with the local Y-axis.
+ * By default, the cone has a radius of 1.0 at the bottom and a height of 2.0
+ */
+ function (ctx) {
+ x3dom.nodeTypes.Cone.superClass.call(this, ctx);
+
+
+ /**
+ * The bottomRadius field specifies the radius of the cone's base.
+ * @var {x3dom.fields.SFFloat} bottomRadius
+ * @range [0, inf]
+ * @memberof x3dom.nodeTypes.Cone
+ * @initvalue 1.0
+ * @field x3d
+ * @instance
+ */
+ this.addField_SFFloat(ctx, 'bottomRadius', 1.0);
+
+ /**
+ * The topRadius field specifies the radius of the cone at the apex.
+ * @var {x3dom.fields.SFFloat} topRadius
+ * @range [0, inf]
+ * @memberof x3dom.nodeTypes.Cone
+ * @initvalue 0
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFFloat(ctx, 'topRadius', 0);
+
+ /**
+ * The height field specifies the height of the cone from the centre of the base to the apex.
+ * @var {x3dom.fields.SFFloat} height
+ * @range [0, inf]
+ * @memberof x3dom.nodeTypes.Cone
+ * @initvalue 2.0
+ * @field x3d
+ * @instance
+ */
+ this.addField_SFFloat(ctx, 'height', 2.0);
+
+ /**
+ * The bottom field specifies whether the bottom cap of the cone is created.
+ * @var {x3dom.fields.SFBool} bottom
+ * @memberof x3dom.nodeTypes.Cone
+ * @initvalue true
+ * @field x3d
+ * @instance
+ */
+ this.addField_SFBool(ctx, 'bottom', true);
+
+ /**
+ * The side field specifies whether sides of the cone are created.
+ * @var {x3dom.fields.SFBool} side
+ * @memberof x3dom.nodeTypes.Cone
+ * @initvalue true
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFBool(ctx, 'side', true);
+
+ /**
+ * The top field specifies whether the top cap of the cone is created.
+ * @var {x3dom.fields.SFBool} top
+ * @memberof x3dom.nodeTypes.Cone
+ * @initvalue true
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFBool(ctx, 'top', true);
+
+ /**
+ * Specifies the number of faces that are generated to approximate the sides of the cone.
+ * @var {x3dom.fields.SFFloat} subdivision
+ * @range [2, inf]
+ * @memberof x3dom.nodeTypes.Cone
+ * @initvalue 32
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFFloat(ctx, 'subdivision', 32);
+
+ var geoCacheID = 'Cone_' + this._vf.bottomRadius + '_' + this._vf.height + '_' + this._vf.top + '_' +
+ this._vf.bottom + '_' + this._vf.side + '_' + this._vf.topRadius + '_' + this._vf.subdivision;
+
+ if (this._vf.useGeoCache && x3dom.geoCache[geoCacheID] !== undefined) {
+ //x3dom.debug.logInfo("Using Cone from Cache");
+ this._mesh = x3dom.geoCache[geoCacheID];
+ }
+ else {
+ var bottomRadius = this._vf.bottomRadius, height = this._vf.height;
+ var topRadius = this._vf.topRadius, sides = this._vf.subdivision;
+
+ var beta, x, z;
+ var delta = 2.0 * Math.PI / sides;
+
+ var incl = (bottomRadius - topRadius) / height;
+ var nlen = 1.0 / Math.sqrt(1.0 + incl * incl);
+
+ var j = 0, k = 0;
+ var h, base;
+
+ if (this._vf.side && height > 0) {
+ var px = 0, pz = 0;
+
+ for (j = 0, k = 0; j <= sides; j++) {
+ beta = j * delta;
+ x = Math.sin(beta);
+ z = -Math.cos(beta);
+
+ if (topRadius > x3dom.fields.Eps) {
+ px = x * topRadius;
+ pz = z * topRadius;
+ }
+
+ this._mesh._positions[0].push(px, height / 2, pz);
+ this._mesh._normals[0].push(x / nlen, incl / nlen, z / nlen);
+ this._mesh._texCoords[0].push(1.0 - j / sides, 1);
+
+ this._mesh._positions[0].push(x * bottomRadius, -height / 2, z * bottomRadius);
+ this._mesh._normals[0].push(x / nlen, incl / nlen, z / nlen);
+ this._mesh._texCoords[0].push(1.0 - j / sides, 0);
+
+ if (j > 0) {
+ this._mesh._indices[0].push(k );
+ this._mesh._indices[0].push(k + 2);
+ this._mesh._indices[0].push(k + 1);
+
+ this._mesh._indices[0].push(k + 1);
+ this._mesh._indices[0].push(k + 2);
+ this._mesh._indices[0].push(k + 3);
+
+ k += 2;
+ }
+ }
+ }
+
+ if (this._vf.bottom && bottomRadius > 0) {
+ base = this._mesh._positions[0].length / 3;
+
+ for (j = sides - 1; j >= 0; j--) {
+ beta = j * delta;
+ x = bottomRadius * Math.sin(beta);
+ z = -bottomRadius * Math.cos(beta);
+
+ this._mesh._positions[0].push(x, -height / 2, z);
+ this._mesh._normals[0].push(0, -1, 0);
+ this._mesh._texCoords[0].push(x / bottomRadius / 2 + 0.5, z / bottomRadius / 2 + 0.5);
+ }
+
+ h = base + 1;
+
+ for (j = 2; j < sides; j++) {
+ this._mesh._indices[0].push(h);
+ this._mesh._indices[0].push(base);
+
+ h = base + j;
+ this._mesh._indices[0].push(h);
+ }
+ }
+
+ if (this._vf.top && topRadius > x3dom.fields.Eps) {
+ base = this._mesh._positions[0].length / 3;
+
+ for (j = sides - 1; j >= 0; j--) {
+ beta = j * delta;
+ x = topRadius * Math.sin(beta);
+ z = -topRadius * Math.cos(beta);
+
+ this._mesh._positions[0].push(x, height / 2, z);
+ this._mesh._normals[0].push(0, 1, 0);
+ this._mesh._texCoords[0].push(x / topRadius / 2 + 0.5, 1.0 - z / topRadius / 2 + 0.5);
+ }
+
+ h = base + 1;
+
+ for (j = 2; j < sides; j++) {
+ this._mesh._indices[0].push(base);
+ this._mesh._indices[0].push(h);
+
+ h = base + j;
+ this._mesh._indices[0].push(h);
+ }
+ }
+
+ this._mesh._invalidate = true;
+ this._mesh._numFaces = this._mesh._indices[0].length / 3;
+ this._mesh._numCoords = this._mesh._positions[0].length / 3;
+
+ x3dom.geoCache[geoCacheID] = this._mesh;
+ }
+
+ },
+ {
+ fieldChanged: function (fieldName)
+ {
+ if (fieldName == "bottomRadius" || fieldName == "topRadius" ||
+ fieldName == "height" || fieldName == "subdivision" ||
+ fieldName == "bottom" || fieldName == "top" || fieldName == "side")
+ {
+ this._mesh._positions[0] = [];
+ this._mesh._indices[0] = [];
+ this._mesh._normals[0] = [];
+ this._mesh._texCoords[0] = [];
+
+ var bottomRadius = this._vf.bottomRadius, height = this._vf.height;
+ var topRadius = this._vf.topRadius, sides = this._vf.subdivision;
+
+ var beta, x, z;
+ var delta = 2.0 * Math.PI / sides;
+
+ var incl = (bottomRadius - topRadius) / height;
+ var nlen = 1.0 / Math.sqrt(1.0 + incl * incl);
+
+ var j = 0, k = 0;
+ var h, base;
+
+ if (this._vf.side && height > 0)
+ {
+ var px = 0, pz = 0;
+
+ for (j = 0, k = 0; j <= sides; j++) {
+ beta = j * delta;
+ x = Math.sin(beta);
+ z = -Math.cos(beta);
+
+ if (topRadius > x3dom.fields.Eps) {
+ px = x * topRadius;
+ pz = z * topRadius;
+ }
+
+ this._mesh._positions[0].push(px, height / 2, pz);
+ this._mesh._normals[0].push(x / nlen, incl / nlen, z / nlen);
+ this._mesh._texCoords[0].push(1.0 - j / sides, 1);
+
+ this._mesh._positions[0].push(x * bottomRadius, -height / 2, z * bottomRadius);
+ this._mesh._normals[0].push(x / nlen, incl / nlen, z / nlen);
+ this._mesh._texCoords[0].push(1.0 - j / sides, 0);
+
+ if (j > 0) {
+ this._mesh._indices[0].push(k );
+ this._mesh._indices[0].push(k + 2);
+ this._mesh._indices[0].push(k + 1);
+
+ this._mesh._indices[0].push(k + 1);
+ this._mesh._indices[0].push(k + 2);
+ this._mesh._indices[0].push(k + 3);
+
+ k += 2;
+ }
+ }
+ }
+
+ if (this._vf.bottom && bottomRadius > 0)
+ {
+ base = this._mesh._positions[0].length / 3;
+
+ for (j = sides - 1; j >= 0; j--) {
+ beta = j * delta;
+ x = bottomRadius * Math.sin(beta);
+ z = -bottomRadius * Math.cos(beta);
+
+ this._mesh._positions[0].push(x, -height / 2, z);
+ this._mesh._normals[0].push(0, -1, 0);
+ this._mesh._texCoords[0].push(x / bottomRadius / 2 + 0.5, z / bottomRadius / 2 + 0.5);
+ }
+
+ h = base + 1;
+
+ for (j = 2; j < sides; j++) {
+ this._mesh._indices[0].push(h);
+ this._mesh._indices[0].push(base);
+
+ h = base + j;
+ this._mesh._indices[0].push(h);
+ }
+ }
+
+ if (this._vf.top && topRadius > x3dom.fields.Eps)
+ {
+ base = this._mesh._positions[0].length / 3;
+
+ for (j = sides - 1; j >= 0; j--) {
+ beta = j * delta;
+ x = topRadius * Math.sin(beta);
+ z = -topRadius * Math.cos(beta);
+
+ this._mesh._positions[0].push(x, height / 2, z);
+ this._mesh._normals[0].push(0, 1, 0);
+ this._mesh._texCoords[0].push(x / topRadius / 2 + 0.5, 1.0 - z / topRadius / 2 + 0.5);
+ }
+
+ h = base + 1;
+
+ for (j = 2; j < sides; j++) {
+ this._mesh._indices[0].push(base);
+ this._mesh._indices[0].push(h);
+
+ h = base + j;
+ this._mesh._indices[0].push(h);
+ }
+ }
+
+ this.invalidateVolume();
+ this._mesh._numFaces = this._mesh._indices[0].length / 3;
+ this._mesh._numCoords = this._mesh._positions[0].length / 3;
+
+ Array.forEach(this._parentNodes, function (node) {
+ node.setAllDirty();
+ node.invalidateVolume();
+ });
+ }
+ }
+ }
+ )
+);
+/** @namespace x3dom.nodeTypes */
+/*
+ * X3DOM JavaScript Library
+ * http://www.x3dom.org
+ *
+ * (C)2009 Fraunhofer IGD, Darmstadt, Germany
+ * Dual licensed under the MIT and GPL
+ */
+
+/* ### Cylinder ### */
+x3dom.registerNodeType(
+ "Cylinder",
+ "Geometry3D",
+ defineClass(x3dom.nodeTypes.X3DSpatialGeometryNode,
+
+ /**
+ * Constructor for Cylinder
+ * @constructs x3dom.nodeTypes.Cylinder
+ * @x3d 3.3
+ * @component Geometry3D
+ * @status experimental
+ * @extends x3dom.nodeTypes.X3DSpatialGeometryNode
+ * @param {Object} [ctx=null] - context object, containing initial settings like namespace
+ * @classdesc The Cylinder node specifies a capped cylinder centred at (0,0,0) in the local coordinate system and with a central axis oriented along the local Y-axis.
+ * By default, the cylinder is sized at "-1" to "+1" in all three dimensions.
+ */
+ function (ctx) {
+ x3dom.nodeTypes.Cylinder.superClass.call(this, ctx);
+
+
+ /**
+ * The radius field specifies the radius of the cylinder.
+ * @var {x3dom.fields.SFFloat} radius
+ * @range [0, inf]
+ * @memberof x3dom.nodeTypes.Cylinder
+ * @initvalue 1.0
+ * @field x3d
+ * @instance
+ */
+ this.addField_SFFloat(ctx, 'radius', 1.0);
+
+ /**
+ * The height field specifies the height of the cylinder along the central axis.
+ * @var {x3dom.fields.SFFloat} height
+ * @range [0, inf]
+ * @memberof x3dom.nodeTypes.Cylinder
+ * @initvalue 2.0
+ * @field x3d
+ * @instance
+ */
+ this.addField_SFFloat(ctx, 'height', 2.0);
+
+ /**
+ * The bottom field specifies whether the bottom cap of the cylinder is created.
+ * @var {x3dom.fields.SFBool} bottom
+ * @memberof x3dom.nodeTypes.Cylinder
+ * @initvalue true
+ * @field x3d
+ * @instance
+ */
+ this.addField_SFBool(ctx, 'bottom', true);
+
+ /**
+ * The top field specifies whether the top cap of the cylinder is created.
+ * @var {x3dom.fields.SFBool} top
+ * @memberof x3dom.nodeTypes.Cylinder
+ * @initvalue true
+ * @field x3d
+ * @instance
+ */
+ this.addField_SFBool(ctx, 'top', true);
+
+ /**
+ * Specifies the number of faces that are generated to approximate the sides of the cylinder.
+ * @var {x3dom.fields.SFFloat} subdivision
+ * @range [2, inf]
+ * @memberof x3dom.nodeTypes.Cylinder
+ * @initvalue 32
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFFloat(ctx, 'subdivision', 32);
+
+ /**
+ * The side field specifies whether sides of the cylinder are created.
+ * @var {x3dom.fields.SFBool} side
+ * @memberof x3dom.nodeTypes.Cylinder
+ * @initvalue true
+ * @field x3d
+ * @instance
+ */
+ this.addField_SFBool(ctx, 'side', true);
+
+ var sides = this._vf.subdivision;
+
+ var geoCacheID = 'Cylinder_'+this._vf.radius+'_'+this._vf.height+'_'+this._vf.bottom+'_'+this._vf.top+'_'+
+ this._vf.side+'_'+this._vf.subdivision;
+
+ if( this._vf.useGeoCache && x3dom.geoCache[geoCacheID] !== undefined )
+ {
+ //x3dom.debug.logInfo("Using Cylinder from Cache");
+ this._mesh = x3dom.geoCache[geoCacheID];
+ }
+ else
+ {
+ var radius = this._vf.radius;
+ var height = this._vf.height / 2;
+
+ var beta, x, z;
+ var delta = 2.0 * Math.PI / sides;
+ var j, k;
+
+ if (this._vf.side)
+ {
+ for (j=0, k=0; j<=sides; j++)
+ {
+ beta = j * delta;
+ x = Math.sin(beta);
+ z = -Math.cos(beta);
+
+ this._mesh._positions[0].push(x * radius, -height, z * radius);
+ this._mesh._normals[0].push(x, 0, z);
+ this._mesh._texCoords[0].push(1.0 - j / sides, 0);
+
+ this._mesh._positions[0].push(x * radius, height, z * radius);
+ this._mesh._normals[0].push(x, 0, z);
+ this._mesh._texCoords[0].push(1.0 - j / sides, 1);
+
+ if (j > 0)
+ {
+ this._mesh._indices[0].push(k );
+ this._mesh._indices[0].push(k + 1);
+ this._mesh._indices[0].push(k + 2);
+
+ this._mesh._indices[0].push(k + 2);
+ this._mesh._indices[0].push(k + 1);
+ this._mesh._indices[0].push(k + 3);
+
+ k += 2;
+ }
+ }
+ }
+
+ if (radius > 0)
+ {
+ var h, base = this._mesh._positions[0].length / 3;
+
+ if (this._vf.top)
+ {
+ for (j=sides-1; j>=0; j--)
+ {
+ beta = j * delta;
+ x = radius * Math.sin(beta);
+ z = -radius * Math.cos(beta);
+
+ this._mesh._positions[0].push(x, height, z);
+ this._mesh._normals[0].push(0, 1, 0);
+ this._mesh._texCoords[0].push(x / radius / 2 + 0.5, -z / radius / 2 + 0.5);
+ }
+
+ h = base + 1;
+
+ for (j=2; j<sides; j++)
+ {
+ this._mesh._indices[0].push(base);
+ this._mesh._indices[0].push(h);
+
+ h = base + j;
+ this._mesh._indices[0].push(h);
+ }
+
+ base = this._mesh._positions[0].length / 3;
+ }
+
+ if (this._vf.bottom)
+ {
+ for (j=sides-1; j>=0; j--)
+ {
+ beta = j * delta;
+ x = radius * Math.sin(beta);
+ z = -radius * Math.cos(beta);
+
+ this._mesh._positions[0].push(x, -height, z);
+ this._mesh._normals[0].push(0, -1, 0);
+ this._mesh._texCoords[0].push(x / radius / 2 + 0.5, z / radius / 2 + 0.5);
+ }
+
+ h = base + 1;
+
+ for (j=2; j<sides; j++)
+ {
+ this._mesh._indices[0].push(h);
+ this._mesh._indices[0].push(base);
+
+ h = base + j;
+ this._mesh._indices[0].push(h);
+ }
+ }
+ }
+
+ this._mesh._invalidate = true;
+ this._mesh._numFaces = this._mesh._indices[0].length / 3;
+ this._mesh._numCoords = this._mesh._positions[0].length / 3;
+
+ x3dom.geoCache[geoCacheID] = this._mesh;
+ }
+
+ },
+ {
+ fieldChanged: function(fieldName) {
+ if (fieldName === "radius" || fieldName === "height")
+ {
+ this._mesh._positions[0] = [];
+
+ var radius = this._vf.radius, height = this._vf.height / 2;
+ var sides = this._vf.subdivision;
+
+ var beta, x, z, j;
+ var delta = 2.0 * Math.PI / sides;
+
+ if (this._vf.side)
+ {
+ for (j=0; j<=sides; j++)
+ {
+ beta = j * delta;
+ x = Math.sin(beta);
+ z = -Math.cos(beta);
+
+ this._mesh._positions[0].push(x * radius, -height, z * radius);
+ this._mesh._positions[0].push(x * radius, height, z * radius);
+ }
+ }
+
+ if (radius > 0)
+ {
+ var h, base = this._mesh._positions[0].length / 3;
+
+ if (this._vf.top)
+ {
+ for (j=sides-1; j>=0; j--)
+ {
+ beta = j * delta;
+ x = radius * Math.sin(beta);
+ z = -radius * Math.cos(beta);
+
+ this._mesh._positions[0].push(x, height, z);
+ }
+ }
+ }
+
+ if (this._vf.bottom)
+ {
+ for (j=sides-1; j>=0; j--)
+ {
+ beta = j * delta;
+ x = radius * Math.sin(beta);
+ z = -radius * Math.cos(beta);
+
+ this._mesh._positions[0].push(x, -height, z);
+ }
+ }
+
+ this.invalidateVolume();
+ this._mesh._numCoords = this._mesh._positions[0].length / 3;
+
+ Array.forEach(this._parentNodes, function (node) {
+ node._dirty.positions = true;
+ node.invalidateVolume();
+ });
+ }
+ else if (fieldName === "subdivision" || fieldName === "bottom" ||
+ fieldName === "top" || fieldName === "side")
+ {
+ this._mesh._positions[0] = [];
+ this._mesh._indices[0] =[];
+ this._mesh._normals[0] = [];
+ this._mesh._texCoords[0] =[];
+
+ var radius = this._vf.radius, height = this._vf.height / 2;
+ var sides = this._vf.subdivision;
+
+ var beta, x, z, j;
+ var delta = 2.0 * Math.PI / sides;
+ var k = 0;
+
+ if (this._vf.side)
+ {
+ for (j=0, k=0; j<=sides; j++)
+ {
+ beta = j * delta;
+ x = Math.sin(beta);
+ z = -Math.cos(beta);
+
+ this._mesh._positions[0].push(x * radius, -height, z * radius);
+ this._mesh._normals[0].push(x, 0, z);
+ this._mesh._texCoords[0].push(1.0 - j / sides, 0);
+
+ this._mesh._positions[0].push(x * radius, height, z * radius);
+ this._mesh._normals[0].push(x, 0, z);
+ this._mesh._texCoords[0].push(1.0 - j / sides, 1);
+
+ if (j > 0)
+ {
+ this._mesh._indices[0].push(k + 0);
+ this._mesh._indices[0].push(k + 1);
+ this._mesh._indices[0].push(k + 2);
+
+ this._mesh._indices[0].push(k + 2);
+ this._mesh._indices[0].push(k + 1);
+ this._mesh._indices[0].push(k + 3);
+
+ k += 2;
+ }
+ }
+ }
+
+ if (radius > 0)
+ {
+ var h, base = this._mesh._positions[0].length / 3;
+
+ if (this._vf.top)
+ {
+ for (j=sides-1; j>=0; j--)
+ {
+ beta = j * delta;
+ x = radius * Math.sin(beta);
+ z = -radius * Math.cos(beta);
+
+ this._mesh._positions[0].push(x, height, z);
+ this._mesh._normals[0].push(0, 1, 0);
+ this._mesh._texCoords[0].push(x / radius / 2 + 0.5, -z / radius / 2 + 0.5);
+ }
+
+ h = base + 1;
+
+ for (j=2; j<sides; j++)
+ {
+ this._mesh._indices[0].push(base);
+ this._mesh._indices[0].push(h);
+
+ h = base + j;
+ this._mesh._indices[0].push(h);
+ }
+
+ base = this._mesh._positions[0].length / 3;
+ }
+
+ if (this._vf.bottom)
+ {
+ for (j=sides-1; j>=0; j--)
+ {
+ beta = j * delta;
+ x = radius * Math.sin(beta);
+ z = -radius * Math.cos(beta);
+
+ this._mesh._positions[0].push(x, -height, z);
+ this._mesh._normals[0].push(0, -1, 0);
+ this._mesh._texCoords[0].push(x / radius / 2 + 0.5, z / radius / 2 + 0.5);
+ }
+
+ h = base + 1;
+
+ for (j=2; j<sides; j++)
+ {
+ this._mesh._indices[0].push(h);
+ this._mesh._indices[0].push(base);
+
+ h = base + j;
+ this._mesh._indices[0].push(h);
+ }
+ }
+ }
+
+ this.invalidateVolume();
+ this._mesh._numFaces = this._mesh._indices[0].length / 3;
+ this._mesh._numCoords = this._mesh._positions[0].length / 3;
+
+ Array.forEach(this._parentNodes, function (node) {
+ node.setAllDirty();
+ node.invalidateVolume();
+ });
+ }
+ }
+ }
+ )
+);
+/** @namespace x3dom.nodeTypes */
+/*
+ * X3DOM JavaScript Library
+ * http://www.x3dom.org
+ *
+ * (C)2009 Fraunhofer IGD, Darmstadt, Germany
+ * Dual licensed under the MIT and GPL
+ */
+
+
+/* ### ExternalGeometry ### */
+x3dom.registerNodeType(
+ "ExternalGeometry",
+ "Geometry3D",
+ defineClass(x3dom.nodeTypes.X3DSpatialGeometryNode,
+
+ /**
+ * Constructor for ExternalGeometry
+ * @constructs x3dom.nodeTypes.ExternalGeometry
+ * @x3d x.x
+ * @component Geometry3D
+ * @extends x3dom.nodeTypes.X3DSpatialGeometryNode
+ * @param {Object} [ctx=null] - context object, containing initial settings like namespace
+ * @classdesc The ExternalGeometry node loads data from a Shape Resource Container (SRC). The used data can be progressively updated during transmission.
+ */
+ function (ctx) {
+ x3dom.nodeTypes.ExternalGeometry.superClass.call(this, ctx);
+
+ /**
+ * Defines the url to the Shape Resource Container. A suffix with a leading # can be used to reference single meshes inside a SRC: "path/to/data.src#mesh0".
+ * @var {x3dom.fields.SFString} url
+ * @memberof x3dom.nodeTypes.Geometry3D
+ * @initvalue ""
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFString(ctx, 'url', "");
+
+
+ //initialization of rendering-related X3DOM structures
+ this._mesh._invalidate = false;
+ this._mesh._numCoords = 0;
+ this._mesh._numFaces = 0;
+ },
+ {
+ //----------------------------------------------------------------------------------------------------------
+ // PUBLIC FUNCTIONS
+ //----------------------------------------------------------------------------------------------------------
+
+ /**
+ * Updates the render data, stored in the given objects, with data from this ExternalGeometry.
+ * If necessary, the referenced file is downloaded first.
+ *
+ * @param {Object} shape - x3dom shape node
+ * @param {Object} shaderProgram - x3dom shader program
+ * @param {Object} gl - WebGL context
+ * @param {Object} viewarea - x3dom view area
+ * @param {Object} context - x3dom context object
+ */
+ updateRenderData: function(shape, shaderProgram, gl, viewarea, context) {
+ var that = this;
+ var xhr;
+
+ if (this._vf['url'] == "") {
+ return;
+ }
+
+ //check if there is still memory available
+ if (x3dom.BinaryContainerLoader.outOfMemory) {
+ return;
+ }
+
+ //TODO: check SOURCE child nodes
+ shape._webgl.internalDownloadCount = 1;
+ shape._nameSpace.doc.downloadCount = 1;
+
+ //TODO: check this object - when is it called, where is it really needed?
+ //shape._webgl.makeSeparateTris = {...};
+
+
+ //post request
+ xhr = new XMLHttpRequest();
+
+ xhr.open("GET", this._vf['url'], true);
+
+ xhr.responseType = "arraybuffer";
+
+ xhr.send(null);
+
+ xhr.onerror = function() {
+ x3dom.debug.logError("Unable to load SRC data from URL \"" + that._vf['url'] + "\"");
+ };
+
+ //TODO: currently, we assume that the referenced file is always an SRC file
+ xhr.onload = function() {
+ shape._webgl.internalDownloadCount = 0;
+ shape._nameSpace.doc.downloadCount = 0;
+
+ var responseBeginUint32 = new Uint32Array(xhr.response, 0, 12);
+
+ var srcHeaderSize, srcBodySize, srcBodyOffset;
+ var srcHeaderView, srcBodyView;
+
+ var srcHeaderObj;
+
+ if ((xhr.status == 200 || xhr.status == 0) && responseBeginUint32.length >= 3) {
+
+ srcHeaderSize = responseBeginUint32[2];
+ srcBodyOffset = srcHeaderSize + 12;
+ srcBodySize = xhr.response.byteLength - srcBodyOffset;
+
+ if (srcHeaderSize > 0 && srcBodySize >= 0)
+ {
+ srcHeaderView = new Uint8Array(xhr.response, 12, srcHeaderSize);
+ srcBodyView = new Uint8Array(xhr.response, srcBodyOffset, srcBodySize );
+
+ //decode SRC header
+ //currently, we assume ASCII JSON encoding
+ try
+ {
+ srcHeaderObj = JSON.parse(String.fromCharCode.apply(null, srcHeaderView));
+ }
+ catch (exc)
+ {
+ x3dom.debug.logError("Unable to parse SRC header: " + exc);
+ return;
+ }
+
+ that._updateRenderDataFromSRC(shape, shaderProgram, gl, srcHeaderObj, srcBodyView);
+ }
+ else
+ {
+ x3dom.debug.logError("Invalid SRC data, loaded from URL \"" +
+ that._vf['url'] + "\"");
+ return;
+ }
+ }
+ else
+ {
+ x3dom.debug.logError("Unable to load SRC data from URL \"" + that._vf['url'] + "\"");
+ }
+ };
+ } ,
+
+ //----------------------------------------------------------------------------------------------------------
+
+ //----------------------------------------------------------------------------------------------------------
+ // PRIVATE FUNCTIONS
+ //----------------------------------------------------------------------------------------------------------
+
+ //TODO: we currently assume that we always read data from exactly one SRC (i.e., no Source nodes)
+ /**
+ * Helper function, updating the render data, stored in the given objects,
+ * with data read from the given SRC.
+ *
+ * @param {Object} shape - x3dom shape node
+ * @param {Object} shaderProgram - x3dom shader program
+ * @param {Object} gl - WebGL context
+ * @param {Object} srcHeaderObj - the JS object which was created from the SRC header
+ * @param {Uint8Array} srcBodyView - a typed array view on the body of the SRC file
+ * @private
+ */
+ _updateRenderDataFromSRC: function(shape, shaderProgram, gl, srcHeaderObj, srcBodyView)
+ {
+ var INDEX_BUFFER_IDX = 0;
+ var POSITION_BUFFER_IDX = 1;
+ var NORMAL_BUFFER_IDX = 2;
+ var TEXCOORD_BUFFER_IDX = 3;
+ var COLOR_BUFFER_IDX = 4;
+ var ID_BUFFER_IDX = 5;
+
+ var MAX_NUM_BUFFERS_PER_DRAW = 6;
+
+ var indexViews = srcHeaderObj["accessors"]["indexViews"];
+ var indexViewID, indexView;
+
+ var attributeViews = srcHeaderObj["accessors"]["attributeViews"];
+ var attributes;
+ var attributeID, attributeView;
+ var x3domTypeID, x3domShortTypeID, numComponents;
+
+ var meshes = srcHeaderObj["meshes"];
+ var mesh, meshID;
+ var meshIdx, bufferOffset;
+
+
+ //the meta data object is currently unused
+ //var metadataObj = srcHeaderObj["meta"];
+
+
+ //1. create GL buffers for bufferChunks / bufferViews
+
+ //create buffers and GL buffer views, and store their identifiers in a map
+ var viewIDsToGLBufferIDs = {};
+
+ //due to the differentiation between targets ARRAY and ELEMENT_ARRAY, we need to check the usage
+ //of the buffer view objects here first, before uploading them for the matching target
+ var indexViewBufferIDs = {};
+ for (indexViewID in indexViews)
+ {
+ indexView = indexViews[indexViewID];
+ indexViewBufferIDs[indexView["bufferView"]] = true;
+ }
+
+ this._createGLBuffersFromSRCChunks(gl,
+ srcHeaderObj["bufferChunks"], srcHeaderObj["bufferViews"],
+ srcBodyView, indexViewBufferIDs, viewIDsToGLBufferIDs);
+
+
+ //2. remember GL index buffer properties, if any
+
+ for (indexViewID in indexViews)
+ {
+ indexView = indexViews[indexViewID];
+
+ //we currently assume 16 bit index data
+ if (indexView["componentType"] != gl.UNSIGNED_SHORT)
+ {
+ x3dom.debug.logWarning("SRC index componentType " + indexView["componentType"] +
+ " is not UNSIGNED_SHORT. " +
+ "Ignoring given value and assuming UNSIGNED_SHORT indices.");
+ }
+ shape._webgl.indexType = gl.UNSIGNED_SHORT;
+ }
+
+
+ //3. remember necessary information to setup GL draw parameters and attribute pointers
+
+ meshIdx = 0;
+ bufferOffset = 0;
+
+ shape._webgl.primType = [];
+ shape._webgl.indexOffset = [];
+ shape._webgl.drawCount = [];
+
+ //hints for stats display
+ this._mesh._numCoords = 0;
+ this._mesh._numFaces = 0;
+
+ for (meshID in meshes)
+ {
+ mesh = meshes[meshID];
+
+ //setup indices, if any
+ indexViewID = mesh["indices"];
+ //TODO: allow the renderer to switch between indexed and non-indexed rendering, for one extGeo
+ if (indexViewID != "")
+ {
+ shape._webgl.externalGeometry = 1; //indexed EG
+
+ indexView = indexViews[indexViewID];
+
+ shape._webgl.indexOffset[meshIdx] = indexView["byteOffset"];
+ shape._webgl.drawCount[meshIdx] = indexView["count"];
+
+ shape._webgl.buffers[INDEX_BUFFER_IDX + bufferOffset] =
+ viewIDsToGLBufferIDs[indexView["bufferView"]];
+
+ //TODO: add support for LINES and POINTS
+ this._mesh._numFaces += indexView["count"] / 3;
+ }
+ else
+ {
+ shape._webgl.externalGeometry = -1; //non-indexed EG
+ }
+
+ //setup primType
+ shape._webgl.primType[meshIdx] = mesh["primitive"];
+
+ //setup attributes
+ attributes = mesh["attributes"];
+
+ for (attributeID in attributes)
+ {
+ attributeView = attributeViews[attributes[attributeID]];
+
+ //the current renderer does not support generic vertex attributes, so simply look for useable cases
+ switch (attributeID)
+ {
+ case "position":
+ x3domTypeID = "coord";
+ x3domShortTypeID = "Pos";
+ shape._webgl.buffers[POSITION_BUFFER_IDX + bufferOffset] =
+ viewIDsToGLBufferIDs[attributeView["bufferView"]];
+ //for non-indexed rendering, we assume that all attributes have the same count
+ if (mesh["indices"] == "")
+ {
+ shape._webgl.drawCount[meshIdx] = attributeView["count"];
+ //TODO: add support for LINES and POINTS
+ this._mesh._numFaces += attributeView["count"] / 3;
+ }
+ this._mesh._numCoords += attributeView["count"];
+ break;
+
+ case "normal":
+ x3domTypeID = "normal";
+ x3domShortTypeID = "Norm";
+ shape._webgl.buffers[NORMAL_BUFFER_IDX + bufferOffset] =
+ viewIDsToGLBufferIDs[attributeView["bufferView"]];
+ break;
+
+ case "texcoord":
+ x3domTypeID = "texCoord";
+ x3domShortTypeID = "Tex";
+ shape._webgl.buffers[TEXCOORD_BUFFER_IDX + bufferOffset] =
+ viewIDsToGLBufferIDs[attributeView["bufferView"]];
+ break;
+
+ case "color":
+ x3domTypeID = "color";
+ x3domShortTypeID = "Col";
+ shape._webgl.buffers[COLOR_BUFFER_IDX + bufferOffset] =
+ viewIDsToGLBufferIDs[attributeView["bufferView"]];
+ break;
+
+ case "id":
+ x3domTypeID = "id";
+ x3domShortTypeID = "Id";
+ shape._webgl.buffers[ID_BUFFER_IDX + bufferOffset] =
+ viewIDsToGLBufferIDs[attributeView["bufferView"]];
+ break;
+ }
+
+ shape["_" + x3domTypeID + "StrideOffset"][0] = attributeView["byteStride"];
+ shape["_" + x3domTypeID + "StrideOffset"][1] = attributeView["byteOffset"];
+ shape._webgl[x3domTypeID + "Type"] = attributeView["componentType"];
+
+ numComponents = x3dom.nodeTypes.ExternalGeometry._findNumComponentsForSRCAccessorType(attributeView["type"]);
+ this._mesh["_num" + x3domShortTypeID + "Components"] = numComponents;
+ }
+
+ ++meshIdx;
+ bufferOffset += MAX_NUM_BUFFERS_PER_DRAW;
+ }
+
+
+ //4. notify renderer
+
+ shape._nameSpace.doc.needRender = true;
+
+ x3dom.BinaryContainerLoader.checkError(gl);
+ },
+
+ //----------------------------------------------------------------------------------------------------------
+
+ /**
+ * Helper function, creating WebGL buffers for the given SRC data structures.
+ * The result is stored in the given map from bufferView IDs to GL buffer IDs.
+ *
+ * @param {Object} gl - WebGL context
+ * @param {Object} bufferChunksObj - the SRC header's bufferChunks object
+ * @param {Object} bufferViewsObj - the SRC header's bufferViews object
+ * @param {Uint8Array} srcBodyView - a typed array view on the body of the SRC file
+ * @param {Object} indexViewBufferIDs - an object which holds the IDs of all index data bufferViews
+ * @param {Object} viewIDsToGLBufferIDs - map that will be filled with a GL buffer ID for each bufferView ID
+ * @private
+ */
+ _createGLBuffersFromSRCChunks: function(gl, bufferChunksObj, bufferViewsObj, srcBodyView,
+ indexViewBufferIDs, viewIDsToGLBufferIDs)
+ {
+ var i;
+ var bufferView;
+ var chunkIDList;
+ var bufferType;
+
+ var chunk;
+ var newBuffer;
+ var chunkDataView;
+ var currentChunkDataOffset;
+
+ //for each buffer view object, create and fill a GL buffer from its buffer chunks
+ for (var bufferViewID in bufferViewsObj)
+ {
+ bufferType = (typeof indexViewBufferIDs[bufferViewID] !== 'undefined') ? gl.ELEMENT_ARRAY_BUFFER :
+ gl.ARRAY_BUFFER;
+
+ bufferView = bufferViewsObj[bufferViewID];
+
+ chunkIDList = bufferView["chunks"];
+
+ //case 1: single chunk
+ if (chunkIDList.length == 1)
+ {
+ chunk = bufferChunksObj[chunkIDList[0]];
+
+ chunkDataView = new Uint8Array(srcBodyView.buffer,
+ srcBodyView.byteOffset + chunk["byteOffset"],
+ chunk["byteLength"]);
+
+ newBuffer = gl.createBuffer();
+
+ gl.bindBuffer(bufferType, newBuffer);
+
+ //upload all chunk data to GPU
+ gl.bufferData(bufferType, chunkDataView, gl.STATIC_DRAW);
+
+ viewIDsToGLBufferIDs[bufferViewID] = newBuffer;
+ }
+ //case 2: multiple chunks
+ else
+ {
+ newBuffer = gl.createBuffer();
+
+ gl.bindBuffer(bufferType, newBuffer);
+
+ //reserve GPU memory for all chunks
+ gl.bufferData(bufferType, bufferView["byteLength"], gl.STATIC_DRAW);
+
+ currentChunkDataOffset = 0;
+
+ for (i = 0; i < chunkIDList.length; ++i)
+ {
+ chunk = bufferChunksObj[chunkIDList[i]];
+
+ chunkDataView = new Uint8Array(srcBodyView.buffer,
+ srcBodyView.byteOffset + chunk["byteOffset"],
+ chunk["byteLength"]);
+
+ //upload chunk data to GPU
+ gl.bufferSubData(bufferType, currentChunkDataOffset, chunkDataView);
+
+ currentChunkDataOffset += chunk["byteLength"];
+ }
+
+ viewIDsToGLBufferIDs[bufferViewID] = newBuffer;
+ }
+ }
+ },
+
+ /**
+ * Returns the node's local volume
+ * @returns {x3dom.fields.BoxVolume} the local, axis-aligned bounding volume
+ */
+ getVolume: function()
+ {
+ var vol = this._mesh._vol;
+ var shapeNode;
+
+ if (!vol.isValid())
+ {
+ //an ExternalGeometry node must _always_ be a child of (at least) one shape node
+ //for multiple Shape nodes using a single ExternalGeometry node,
+ //we assume that either all of them, or no one have specified a bounding volume
+ shapeNode = this._parentNodes[0];
+
+ if (typeof shapeNode._vf["bboxCenter"] != 'undefined' &&
+ typeof shapeNode._vf["bboxSize"] != 'undefined' )
+ {
+ vol.setBoundsByCenterSize(shapeNode._vf["bboxCenter"], shapeNode._vf["bboxSize"]);
+ }
+ //if no bbox information was specified for the Shape node, use information from the SRC header
+ else
+ {
+ //TODO: implement
+ }
+ }
+
+ return vol;
+ }
+
+ //----------------------------------------------------------------------------------------------------------
+
+ /*nodeChanged: function()
+ {
+ Array.forEach(this._parentNodes, function (node) {
+ node._dirty.positions = true;
+ node._dirty.normals = true;
+ node._dirty.texcoords = true;
+ node._dirty.colors = true;
+ });
+ this._vol.invalidate();
+ },
+
+ fieldChanged: function(fieldName)
+ {
+ if (fieldName == "index" ||fieldName == "coord" || fieldName == "normal" ||
+ fieldName == "texCoord" || fieldName == "color") {
+ this._dirty[fieldName] = true;
+ this._vol.invalidate();
+ }
+ else if (fieldName == "implicitMeshSize") {
+ this._vol.invalidate();
+ }
+ }*/
+
+ }
+ )
+);
+
+
+//----------------------------------------------------------------------------------------------------------------------
+// PUBLIC STATIC FUNCTIONS
+//----------------------------------------------------------------------------------------------------------------------
+
+
+
+//----------------------------------------------------------------------------------------------------------------------
+
+//----------------------------------------------------------------------------------------------------------------------
+// PRIVATE STATIC FUNCTIONS
+//----------------------------------------------------------------------------------------------------------------------
+
+/**
+ *
+ * @param {STRING} type - accessor type, must be "SCALAR", "VEC2", "VEC3" or "VEC4"
+ * @private
+ */
+x3dom.nodeTypes.ExternalGeometry._findNumComponentsForSRCAccessorType = function(type)
+{
+ switch (type)
+ {
+ case "SCALAR": return 1;
+ case "VEC2": return 2;
+ case "VEC3": return 3;
+ case "VEC4": return 4;
+ default: return 0;
+ }
+};
+
+//----------------------------------------------------------------------------------------------------------------------
+
+/** @namespace x3dom.nodeTypes */
+/*
+ * X3DOM JavaScript Library
+ * http://www.x3dom.org
+ *
+ * (C)2009 Fraunhofer IGD, Darmstadt, Germany
+ * Dual licensed under the MIT and GPL
+ */
+
+/* ### X3DBinaryContainerGeometryNode ### */
+x3dom.registerNodeType(
+ "X3DBinaryContainerGeometryNode",
+ "Geometry3D",
+ defineClass(x3dom.nodeTypes.X3DSpatialGeometryNode,
+
+ /**
+ * Constructor for X3DBinaryContainerGeometryNode
+ * @constructs x3dom.nodeTypes.X3DBinaryContainerGeometryNode
+ * @x3d x.x
+ * @component Geometry3D
+ * @status experimental
+ * @extends x3dom.nodeTypes.X3DSpatialGeometryNode
+ * @param {Object} [ctx=null] - context object, containing initial settings like namespace
+ */
+ function (ctx) {
+ x3dom.nodeTypes.X3DBinaryContainerGeometryNode.superClass.call(this, ctx);
+
+
+ /**
+ *
+ * @var {x3dom.fields.SFVec3f} position
+ * @memberof x3dom.nodeTypes.X3DBinaryContainerGeometryNode
+ * @initvalue 0,0,0
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFVec3f(ctx, 'position', 0, 0, 0);
+
+ /**
+ *
+ * @var {x3dom.fields.SFVec3f} size
+ * @memberof x3dom.nodeTypes.X3DBinaryContainerGeometryNode
+ * @initvalue 1,1,1
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFVec3f(ctx, 'size', 1, 1, 1);
+
+ /**
+ *
+ * @var {x3dom.fields.MFInt32} vertexCount
+ * @memberof x3dom.nodeTypes.X3DBinaryContainerGeometryNode
+ * @initvalue [0]
+ * @field x3dom
+ * @instance
+ */
+ this.addField_MFInt32(ctx, 'vertexCount', [0]);
+
+ /**
+ *
+ * @var {x3dom.fields.MFString} primType
+ * @memberof x3dom.nodeTypes.X3DBinaryContainerGeometryNode
+ * @initvalue ['TRIANGLES']
+ * @field x3dom
+ * @instance
+ */
+ this.addField_MFString(ctx, 'primType', ['TRIANGLES']);
+
+ // correct min/max of bounding volume set in BinaryContainerGeometry
+ this._mesh._invalidate = false;
+ this._mesh._numCoords = 0;
+ this._mesh._numFaces = 0;
+
+ this._diameter = this._vf.size.length();
+
+ },
+ {
+ getMin: function() {
+ var vol = this._mesh._vol;
+
+ if (!vol.isValid()) {
+ vol.setBoundsByCenterSize(this._vf.position, this._vf.size);
+ }
+
+ return vol.min;
+ },
+
+ getMax: function() {
+ var vol = this._mesh._vol;
+
+ if (!vol.isValid()) {
+ vol.setBoundsByCenterSize(this._vf.position, this._vf.size);
+ }
+
+ return vol.max;
+ },
+
+ getVolume: function() {
+ var vol = this._mesh._vol;
+
+ if (!vol.isValid()) {
+ vol.setBoundsByCenterSize(this._vf.position, this._vf.size);
+ }
+
+ return vol;
+ },
+
+ invalidateVolume: function() {
+ // at the moment, do nothing here since field updates are not impl.
+ },
+
+ getCenter: function() {
+ return this._vf.position;
+ },
+
+ getDiameter: function() {
+ return this._diameter;
+ },
+
+ needLighting: function() {
+ var hasTris = (this._vf.primType.length && this._vf.primType[0].indexOf("TRIANGLE") >= 0);
+ return (this._vf.lit && hasTris);
+ }
+ }
+ )
+);
+/** @namespace x3dom.nodeTypes */
+/*
+ * X3DOM JavaScript Library
+ * http://www.x3dom.org
+ *
+ * (C)2009 Fraunhofer IGD, Darmstadt, Germany
+ * Dual licensed under the MIT and GPL
+ */
+
+/* ### BinaryGeometry ### */
+x3dom.registerNodeType(
+ "BinaryGeometry",
+ "Geometry3D",
+ defineClass(x3dom.nodeTypes.X3DBinaryContainerGeometryNode,
+
+ /**
+ * Constructor for BinaryGeometry
+ * @constructs x3dom.nodeTypes.BinaryGeometry
+ * @x3d x.x
+ * @component Geometry3D
+ * @extends x3dom.nodeTypes.X3DBinaryContainerGeometryNode
+ * @param {Object} [ctx=null] - context object, containing initial settings like namespace
+ * @classdesc The BinaryGeometry node can load binary data exported by AOPT.
+ */
+ function (ctx) {
+ x3dom.nodeTypes.BinaryGeometry.superClass.call(this, ctx);
+
+
+ /**
+ * The url to the binary file, that contains the index data.
+ * @var {x3dom.fields.SFString} index
+ * @memberof x3dom.nodeTypes.BinaryGeometry
+ * @initvalue ""
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFString(ctx, 'index', ""); // Uint16
+
+ /**
+ * The url to the binary file, that contains the mesh coordinates.
+ * @var {x3dom.fields.SFString} coord
+ * @memberof x3dom.nodeTypes.BinaryGeometry
+ * @initvalue ""
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFString(ctx, 'coord', ""); // Float32
+
+ /**
+ * The url to the binary file, that contains the normals.
+ * @var {x3dom.fields.SFString} normal
+ * @memberof x3dom.nodeTypes.BinaryGeometry
+ * @initvalue ""
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFString(ctx, 'normal', "");
+
+ /**
+ * The url to the binary file, that contains the texture coordinates.
+ * @var {x3dom.fields.SFString} texCoord
+ * @memberof x3dom.nodeTypes.BinaryGeometry
+ * @initvalue ""
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFString(ctx, 'texCoord', ""); // THINKABOUTME: add texCoord1, texCoord2, ...?
+
+ /**
+ * The url to the binary file, that contains the colors.
+ * @var {x3dom.fields.SFString} color
+ * @memberof x3dom.nodeTypes.BinaryGeometry
+ * @initvalue ""
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFString(ctx, 'color', "");
+
+ /**
+ *
+ * @var {x3dom.fields.SFString} tangent
+ * @memberof x3dom.nodeTypes.BinaryGeometry
+ * @initvalue ""
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFString(ctx, 'tangent', ""); // TODO
+
+ /**
+ *
+ * @var {x3dom.fields.SFString} binormal
+ * @memberof x3dom.nodeTypes.BinaryGeometry
+ * @initvalue ""
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFString(ctx, 'binormal', ""); // TODO
+
+ // Typed Array View Types
+ // Int8, Uint8, Int16, Uint16, Int32, Uint32, Float32, Float64
+
+ /**
+ * Specifies the byte format of the index data.
+ * @var {x3dom.fields.SFString} indexType
+ * @memberof x3dom.nodeTypes.BinaryGeometry
+ * @initvalue "Uint16"
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFString(ctx, 'indexType', "Uint16");
+
+ /**
+ * Specifies the byte format of the coordinates.
+ * @var {x3dom.fields.SFString} coordType
+ * @memberof x3dom.nodeTypes.BinaryGeometry
+ * @initvalue "Float32"
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFString(ctx, 'coordType', "Float32");
+
+ /**
+ * Specifies the byte format of the normals.
+ * @var {x3dom.fields.SFString} normalType
+ * @memberof x3dom.nodeTypes.BinaryGeometry
+ * @initvalue "Float32"
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFString(ctx, 'normalType', "Float32");
+
+ /**
+ * Specifies the byte format of the texture coordinates.
+ * @var {x3dom.fields.SFString} texCoordType
+ * @memberof x3dom.nodeTypes.BinaryGeometry
+ * @initvalue "Float32"
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFString(ctx, 'texCoordType', "Float32");
+
+ /**
+ * Specifies the byte format of the colors.
+ * @var {x3dom.fields.SFString} colorType
+ * @memberof x3dom.nodeTypes.BinaryGeometry
+ * @initvalue "Float32"
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFString(ctx, 'colorType', "Float32");
+
+ /**
+ * Specifies the byte format of the tangents.
+ * @var {x3dom.fields.SFString} tangentType
+ * @memberof x3dom.nodeTypes.BinaryGeometry
+ * @initvalue "Float32"
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFString(ctx, 'tangentType', "Float32");
+
+ /**
+ * Specifies the byte format of the binormals.
+ * @var {x3dom.fields.SFString} binormalType
+ * @memberof x3dom.nodeTypes.BinaryGeometry
+ * @initvalue "Float32"
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFString(ctx, 'binormalType', "Float32");
+
+
+ /**
+ * Specifies whether the normals are encoded as spherical coordinates.
+ * @var {x3dom.fields.SFBool} normalAsSphericalCoordinates
+ * @memberof x3dom.nodeTypes.BinaryGeometry
+ * @initvalue false
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFBool(ctx, 'normalAsSphericalCoordinates', false);
+
+ /**
+ * Enables RGBA colors.
+ * @var {x3dom.fields.SFBool} rgbaColors
+ * @memberof x3dom.nodeTypes.BinaryGeometry
+ * @initvalue false
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFBool(ctx, 'rgbaColors', false);
+
+ /**
+ * Specifies the number of texture coordinates per vertex.
+ * @var {x3dom.fields.SFInt32} numTexCoordComponents
+ * @range [1, inf]
+ * @memberof x3dom.nodeTypes.BinaryGeometry
+ * @initvalue 2
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFInt32(ctx, 'numTexCoordComponents', 2);
+
+ /**
+ * Specifies whether normals are stored per vertex or per face.
+ * @var {x3dom.fields.SFBool} normalPerVertex
+ * @memberof x3dom.nodeTypes.BinaryGeometry
+ * @initvalue true
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFBool(ctx, 'normalPerVertex', true);
+
+ /**
+ * Flag that specifies whether vertex IDs are given as texture coordinates.
+ * @var {x3dom.fields.SFBool} idsPerVertex
+ * @memberof x3dom.nodeTypes.BinaryGeometry
+ * @initvalue false
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFBool(ctx, 'idsPerVertex', false);
+
+ // workaround
+ this._hasStrideOffset = false;
+ this._mesh._numPosComponents = this._vf.normalAsSphericalCoordinates ? 4 : 3;
+ this._mesh._numTexComponents = this._vf.numTexCoordComponents;
+ this._mesh._numColComponents = this._vf.rgbaColors ? 4 : 3;
+ this._mesh._numNormComponents = this._vf.normalAsSphericalCoordinates ? 2 : 3;
+
+ // info helper members
+ this._vertexCountSum = 0;
+ for (var i=0; i<this._vf.vertexCount.length; ++i) {
+ this._vertexCountSum += this._vf.vertexCount[i];
+ }
+
+ },
+ {
+ parentAdded: function(parent)
+ {
+ // TODO; also handle multiple shape parents!
+ var offsetInd, strideInd, offset, stride;
+
+ offsetInd = this._vf.coord.lastIndexOf('#');
+ strideInd = this._vf.coord.lastIndexOf('+');
+ if (offsetInd >= 0 && strideInd >= 0) {
+ offset = +this._vf.coord.substring(++offsetInd, strideInd);
+ stride = +this._vf.coord.substring(strideInd);
+ parent._coordStrideOffset = [stride, offset];
+ this._hasStrideOffset = true;
+ if ((offset / 8) - Math.floor(offset / 8) == 0) {
+ this._mesh._numPosComponents = 4;
+ }
+ //x3dom.debug.logInfo("coord stride/offset: " + stride + ", " + offset);
+ }
+ else if (strideInd >= 0) {
+ stride = +this._vf.coord.substring(strideInd);
+ parent._coordStrideOffset = [stride, 0];
+ if ((stride / 8) - Math.floor(stride / 8) == 0) {
+ this._mesh._numPosComponents = 4; // ???
+ }
+ //x3dom.debug.logInfo("coord stride: " + stride);
+ }
+
+ offsetInd = this._vf.normal.lastIndexOf('#');
+ strideInd = this._vf.normal.lastIndexOf('+');
+ if (offsetInd >= 0 && strideInd >= 0) {
+ offset = +this._vf.normal.substring(++offsetInd, strideInd);
+ stride = +this._vf.normal.substring(strideInd);
+ parent._normalStrideOffset = [stride, offset];
+ //x3dom.debug.logInfo("normal stride/offset: " + stride + ", " + offset);
+ }
+ else if (strideInd >= 0) {
+ stride = +this._vf.normal.substring(strideInd);
+ parent._normalStrideOffset = [stride, 0];
+ //x3dom.debug.logInfo("normal stride: " + stride);
+ }
+
+ offsetInd = this._vf.texCoord.lastIndexOf('#');
+ strideInd = this._vf.texCoord.lastIndexOf('+');
+ if (offsetInd >= 0 && strideInd >= 0) {
+ offset = +this._vf.texCoord.substring(++offsetInd, strideInd);
+ stride = +this._vf.texCoord.substring(strideInd);
+ parent._texCoordStrideOffset = [stride, offset];
+ //x3dom.debug.logInfo("texCoord stride/offset: " + stride + ", " + offset);
+ }
+ else if (strideInd >= 0) {
+ stride = +this._vf.texCoord.substring(strideInd);
+ parent._texCoordStrideOffset = [stride, 0];
+ //x3dom.debug.logInfo("texCoord stride: " + stride);
+ }
+
+ offsetInd = this._vf.color.lastIndexOf('#');
+ strideInd = this._vf.color.lastIndexOf('+');
+ if (offsetInd >= 0 && strideInd >= 0) {
+ offset = +this._vf.color.substring(++offsetInd, strideInd);
+ stride = +this._vf.color.substring(strideInd);
+ parent._colorStrideOffset = [stride, offset];
+ //x3dom.debug.logInfo("color stride/offset: " + stride + ", " + offset);
+ }
+ else if (strideInd >= 0) {
+ stride = +this._vf.color.substring(strideInd);
+ parent._colorStrideOffset = [stride, 0];
+ //x3dom.debug.logInfo("color stride: " + stride);
+ }
+
+ if (this._vf.indexType != "Uint16" && !x3dom.caps.INDEX_UINT)
+ x3dom.debug.logWarning("Index type " + this._vf.indexType + " problematic");
+ },
+
+ doIntersect: function(line)
+ {
+ var min = this.getMin();
+ var max = this.getMax();
+ var isect = line.intersect(min, max);
+
+ if (isect && line.enter < line.dist) {
+ line.dist = line.enter;
+ line.hitObject = this;
+ line.hitPoint = line.pos.add(line.dir.multiply(line.enter));
+ return true;
+ }
+ else {
+ return false;
+ }
+ },
+
+ getPrecisionMax: function(type)
+ {
+ switch(this._vf[type])
+ {
+ case "Int8":
+ return 127.0;
+ case "Uint8":
+ return 255.0;
+ case "Int16":
+ return 32767.0;
+ case "Uint16":
+ return 65535.0;
+ case "Int32":
+ return 2147483647.0;
+ case "Uint32":
+ return 4294967295.0;
+ case "Float32":
+ case "Float64":
+ default:
+ return 1.0;
+ }
+ }
+ }
+ )
+);
+/** @namespace x3dom.nodeTypes */
+/*
+ * X3DOM JavaScript Library
+ * http://www.x3dom.org
+ *
+ * (C)2009 Fraunhofer IGD, Darmstadt, Germany
+ * Dual licensed under the MIT and GPL
+ */
+
+/* ### PopGeometryLevel ### */
+x3dom.registerNodeType(
+ "PopGeometryLevel",
+ "Geometry3D",
+ defineClass(x3dom.nodeTypes.X3DGeometricPropertyNode,
+
+ /**
+ * Constructor for PopGeometryLevel
+ * @constructs x3dom.nodeTypes.PopGeometryLevel
+ * @x3d x.x
+ * @component Geometry3D
+ * @status experimental
+ * @extends x3dom.nodeTypes.X3DGeometricPropertyNode
+ * @param {Object} [ctx=null] - context object, containing initial settings like namespace
+ * @classdesc The PopGeometryLevel node holds data of one refinement level for the PopGeometry node.
+ */
+ function (ctx) {
+ x3dom.nodeTypes.PopGeometryLevel.superClass.call(this, ctx);
+
+
+ /**
+ * Location of the binary file that contains the data of this refinement level.
+ * @var {x3dom.fields.SFString} src
+ * @memberof x3dom.nodeTypes.PopGeometryLevel
+ * @initvalue ""
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFString(ctx, 'src', "");
+
+ /**
+ * Number of indices shipped with this refinement level.
+ * @var {x3dom.fields.SFInt32} numIndices
+ * @memberof x3dom.nodeTypes.PopGeometryLevel
+ * @initvalue 0
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFInt32(ctx, 'numIndices', 0);
+
+ /**
+ * Offset of the interleaved attribute data of this refinement level within the target vertex buffer.
+ * @var {x3dom.fields.SFInt32} vertexDataBufferOffset
+ * @memberof x3dom.nodeTypes.PopGeometryLevel
+ * @initvalue 0
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFInt32(ctx, 'vertexDataBufferOffset', 0);
+
+ },
+ {
+ getSrc: function () {
+ return this._vf.src;
+ },
+
+ getNumIndices: function () {
+ return this._vf.numIndices;
+ },
+
+ getVertexDataBufferOffset: function () {
+ return this._vf.vertexDataBufferOffset;
+ }
+ }
+ )
+);
+/** @namespace x3dom.nodeTypes */
+/*
+ * X3DOM JavaScript Library
+ * http://www.x3dom.org
+ *
+ * (C)2009 Fraunhofer IGD, Darmstadt, Germany
+ * Dual licensed under the MIT and GPL
+ */
+
+/* ### PopGeometry ### */
+x3dom.registerNodeType(
+ "PopGeometry",
+ "Geometry3D",
+ defineClass(x3dom.nodeTypes.X3DBinaryContainerGeometryNode,
+
+ /**
+ * Constructor for PopGeometry
+ * @constructs x3dom.nodeTypes.PopGeometry
+ * @x3d x.x
+ * @component Geometry3D
+ * @status experimental
+ * @extends x3dom.nodeTypes.X3DBinaryContainerGeometryNode
+ * @param {Object} [ctx=null] - context object, containing initial settings like namespace
+ * @classdesc The PopGeometry node provides a first, experimental implementation of the POP Buffer algorithm for progressive streaming of triangular mesh data.
+ */
+ function (ctx) {
+ x3dom.nodeTypes.PopGeometry.superClass.call(this, ctx);
+
+ /**
+ * The size of the bounding box of this geometry, as it is used for culling.
+ * @var {x3dom.fields.SFVec3f} tightSize
+ * @memberof x3dom.nodeTypes.PopGeometry
+ * @initvalue 1,1,1
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFVec3f (ctx, 'tightSize', 1, 1, 1);
+ //@todo: add this on export
+
+ /**
+ * The size of the bounding box used to quantize data in this geometry,
+ * which is usually the largest bounding box of all sub-meshes of a given mesh.
+ * @var {x3dom.fields.SFVec3f} maxBBSize
+ * @memberof x3dom.nodeTypes.PopGeometry
+ * @initvalue 1,1,1
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFVec3f (ctx, 'maxBBSize', 1, 1, 1);
+
+ /**
+ * The minimum coordinates of the bounding box, in a normalized range between [0,1],
+ * and given modulo maxBBSize.
+ * @var {x3dom.fields.SFVec3f} bbMinModF
+ * @memberof x3dom.nodeTypes.PopGeometry
+ * @initvalue 0,0,0
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFVec3f (ctx, 'bbMinModF', 0, 0, 0);
+
+ /**
+ * The maximum coordinates of the bounding box, in a normalized range between [0,1],
+ * and given modulo maxBBSize.
+ * @var {x3dom.fields.SFVec3f} bbMaxModF
+ * @memberof x3dom.nodeTypes.PopGeometry
+ * @initvalue 1,1,1
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFVec3f (ctx, 'bbMaxModF', 1, 1, 1);
+
+ /**
+ * Minimum coordinates of the bounding box, in object coordinates.
+ * @var {x3dom.fields.SFVec3f} bbMin
+ * @memberof x3dom.nodeTypes.PopGeometry
+ * @initvalue 0,0,0
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFVec3f (ctx, 'bbMin', 0, 0, 0);
+
+ /**
+ * Field for internal use.
+ * @var {x3dom.fields.SFVec3f} bbShiftVec
+ * @memberof x3dom.nodeTypes.PopGeometry
+ * @initvalue 0,0,0
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFVec3f (ctx, 'bbShiftVec', 0, 0, 0);
+
+ if (this._vf.bbMinModF.x > this._vf.bbMaxModF.x)
+ this._vf.bbShiftVec.x = 1.0;
+ if (this._vf.bbMinModF.y > this._vf.bbMaxModF.y)
+ this._vf.bbShiftVec.y = 1.0;
+ if (this._vf.bbMinModF.z > this._vf.bbMaxModF.z)
+ this._vf.bbShiftVec.z = 1.0;
+
+
+ /**
+ * Number of levels of this pop geometry.
+ * @var {x3dom.fields.MFNode} levels
+ * @memberof x3dom.nodeTypes.PopGeometry
+ * @initvalue x3dom.nodeTypes.PopGeometryLevel
+ * @field x3dom
+ * @instance
+ */
+ this.addField_MFNode('levels', x3dom.nodeTypes.PopGeometryLevel);
+
+
+ /**
+ * Stride of all (interleaved) attributes, given in bytes.
+ * @var {x3dom.fields.SFInt32} attributeStride
+ * @memberof x3dom.nodeTypes.PopGeometry
+ * @initvalue 0
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFInt32(ctx, 'attributeStride', 0);
+
+ /**
+ * Offset, given in bytes, for the position attribute inside the interleaved attribute array.
+ * @var {x3dom.fields.SFInt32} positionOffset
+ * @memberof x3dom.nodeTypes.PopGeometry
+ * @initvalue 0
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFInt32(ctx, 'positionOffset', 0);
+
+ /**
+ * Offset, given in bytes, for the normal attribute inside the interleaved attribute array.
+ * @var {x3dom.fields.SFInt32} normalOffset
+ * @memberof x3dom.nodeTypes.PopGeometry
+ * @initvalue 0
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFInt32(ctx, 'normalOffset', 0);
+
+ /**
+ * Offset, given in bytes, for the texture coordinate attribute inside the interleaved attribute array.
+ * @var {x3dom.fields.SFInt32} texcoordOffset
+ * @memberof x3dom.nodeTypes.PopGeometry
+ * @initvalue 0
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFInt32(ctx, 'texcoordOffset', 0);
+
+ /**
+ * Offset, given in bytes, for the color attribute inside the interleaved attribute array.
+ * @var {x3dom.fields.SFInt32} colorOffset
+ * @memberof x3dom.nodeTypes.PopGeometry
+ * @initvalue 0
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFInt32(ctx, 'colorOffset', 0);
+
+ /**
+ * Number of anchor vertices (can be 0).
+ * Anchor vertices are used to keep some vertices on the bordes between sub-meshes fixed during refinement.
+ * @var {x3dom.fields.SFInt32} numAnchorVertices
+ * @memberof x3dom.nodeTypes.PopGeometry
+ * @initvalue 0
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFInt32(ctx, 'numAnchorVertices', 0);
+
+
+ /**
+ * Precision, given in bytes, for the components of the position attribute.
+ * @var {x3dom.fields.SFInt32} positionPrecision
+ * @memberof x3dom.nodeTypes.PopGeometry
+ * @initvalue 2
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFInt32(ctx, 'positionPrecision', 2);
+
+ /**
+ * Precision, given in bytes, for the components of the normal attribute.
+ * @var {x3dom.fields.SFInt32} normalPrecision
+ * @memberof x3dom.nodeTypes.PopGeometry
+ * @initvalue 1
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFInt32(ctx, 'normalPrecision', 1);
+
+ /**
+ * Precision, given in bytes, for the components of the texture coordinate attribute.
+ * @var {x3dom.fields.SFInt32} texcoordPrecision
+ * @memberof x3dom.nodeTypes.PopGeometry
+ * @initvalue 2
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFInt32(ctx, 'texcoordPrecision', 2);
+
+ /**
+ * Precision, given in bytes, for the components of the color attribute.
+ * @var {x3dom.fields.SFInt32} colorPrecision
+ * @memberof x3dom.nodeTypes.PopGeometry
+ * @initvalue 1
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFInt32(ctx, 'colorPrecision', 1);
+
+
+ /**
+ * Minimum precision level of this PopGeometry node.
+ * This can be used to clamp displayed precision - if the value is -1, no clamping takes place.
+ * @var {x3dom.fields.SFInt32} minPrecisionLevel
+ * @memberof x3dom.nodeTypes.PopGeometry
+ * @initvalue -1
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFInt32(ctx, 'minPrecisionLevel', -1);
+
+ /**
+ * Maximum precision level of this PopGeometry node.
+ * This can be used to clamp displayed precision - if the value is -1, no clamping takes place.
+ * @var {x3dom.fields.SFInt32} maxPrecisionLevel
+ * @memberof x3dom.nodeTypes.PopGeometry
+ * @initvalue -1
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFInt32(ctx, 'maxPrecisionLevel', -1);
+
+ /**
+ * Additional precision multiplication factor, for tuning the displayed precision.
+ * @var {x3dom.fields.SFFloat} precisionFactor
+ * @memberof x3dom.nodeTypes.PopGeometry
+ * @initvalue 1.0
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFFloat(ctx, 'precisionFactor', 1.0);
+
+ //those four fields are read by the x3dom renderer
+
+ /**
+ * Field for internal use by the X3DOM renderer.
+ * @var {x3dom.fields.SFString} coordType
+ * @memberof x3dom.nodeTypes.PopGeometry
+ * @initvalue "Uint16"
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFString(ctx, 'coordType', "Uint16");
+
+ /**
+ * Field for internal use by the X3DOM renderer.
+ * @var {x3dom.fields.SFString} normalType
+ * @memberof x3dom.nodeTypes.PopGeometry
+ * @initvalue "Uint8"
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFString(ctx, 'normalType', "Uint8");
+
+ /**
+ * Field for internal use by the X3DOM renderer.
+ * @var {x3dom.fields.SFString} texCoordType
+ * @memberof x3dom.nodeTypes.PopGeometry
+ * @initvalue "Uint16"
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFString(ctx, 'texCoordType', "Uint16");
+
+ /**
+ * Field for internal use by the X3DOM renderer.
+ * @var {x3dom.fields.SFString} colorType
+ * @memberof x3dom.nodeTypes.PopGeometry
+ * @initvalue "Uint8"
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFString(ctx, 'colorType', "Uint8");
+
+
+ /**
+ * Size of the vertex buffer, used to pre-allocate the buffer before downloading data.
+ * @var {x3dom.fields.SFInt32} vertexBufferSize
+ * @memberof x3dom.nodeTypes.PopGeometry
+ * @initvalue 0
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFInt32(ctx, 'vertexBufferSize', 0);
+
+
+ /**
+ * Specifies whether this PopGeometry was encoded for indexed rendering.
+ * @var {x3dom.fields.SFBool} indexedRendering
+ * @memberof x3dom.nodeTypes.PopGeometry
+ * @initvalue true
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFBool(ctx, 'indexedRendering', true);
+ //ATTENTION: Although it might be supported by aopt,
+ // X3DOM does not accept 16 bit spherical normals yet,
+ // spherical normals are assumed to be 8 bit and get
+ // encoded as the 4th 16 bit position component
+
+ /**
+ * Specifies whether this PopGeometry was encoded for rendering with spherical normals.
+ * @var {x3dom.fields.SFBool} sphericalNormals
+ * @memberof x3dom.nodeTypes.PopGeometry
+ * @initvalue false
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFBool(ctx, 'sphericalNormals', false);
+
+ //needed as we manipulate vertexCount during loading
+
+ /**
+ * Vertex count at the highest possible level of precision.
+ * @var {x3dom.fields.MFInt32} originalVertexCount
+ * @memberof x3dom.nodeTypes.PopGeometry
+ * @initvalue [0]
+ * @field x3dom
+ * @instance
+ */
+ this.addField_MFInt32(ctx, 'originalVertexCount', [0]);
+
+ for (var i = 0; i < this._vf.vertexCount.length; ++i) {
+ this._vf.originalVertexCount[i] = this._vf.vertexCount[i];
+ }
+
+ //@todo: remove this three lines after cleanup
+ this._vf.maxBBSize = x3dom.fields.SFVec3f.copy(this._vf.size);
+ this._vf.size = this._vf.tightSize;
+ this._diameter = this._vf.size.length();
+
+ this._bbMinBySize = [ Math.floor(this._vf.bbMin.x / this._vf.maxBBSize.x),
+ Math.floor(this._vf.bbMin.y / this._vf.maxBBSize.y),
+ Math.floor(this._vf.bbMin.z / this._vf.maxBBSize.z) ];
+ this._volRadius = this._vf.size.length() / 2;
+ this._volLargestRadius = this._vf.maxBBSize.length() / 2;
+
+ // workaround
+ this._mesh._numPosComponents = this._vf.sphericalNormals ? 4 : 3;
+ this._mesh._numNormComponents = this._vf.sphericalNormals ? 2 : 3;
+ this._mesh._numTexComponents = 2;
+ this._mesh._numColComponents = 3;
+
+ x3dom.nodeTypes.PopGeometry.numTotalVerts += this.getVertexCount();
+ x3dom.nodeTypes.PopGeometry.numTotalTris += (this.hasIndex() ?
+ this.getTotalNumberOfIndices() : this.getVertexCount()) / 3;
+
+ },
+ {
+ forceUpdateCoverage: function() {
+ return true;
+ },
+
+ getBBoxShiftVec: function() {
+ return this._vf.bbShiftVec;
+ },
+
+ getBBoxSize: function() {
+ return this._vf.size;
+ },
+
+ hasIndex: function() {
+ return this._vf.indexedRendering;
+ },
+
+ getTotalNumberOfIndices: function() {
+ if (this._vf.indexedRendering) {
+ var sum = 0;
+ for (var i = 0; i < this._vf.originalVertexCount.length; ++i) {
+ sum += this._vf.originalVertexCount[i];
+ }
+ return sum;
+ }
+ else {
+ return 0;
+ }
+ },
+
+ getVertexCount: function() {
+ var sum = 0;
+ for (var i = 0; i < this._vf.originalVertexCount.length; ++i) {
+ sum += this._vf.originalVertexCount[i];
+ }
+ return sum;
+ },
+
+ //adapts the vertex count according to the given total number of indices / vertices
+ //which is used by the renderer
+ adaptVertexCount: function(numVerts) {
+ var verts = 0;
+ for (var i = 0; i < this._vf.originalVertexCount.length; ++i) {
+ if ((this._vf.originalVertexCount[i] + verts) <= numVerts) {
+ this._vf.vertexCount[i] = this._vf.originalVertexCount[i];
+ verts += this._vf.originalVertexCount[i];
+ }
+ else {
+ this._vf.vertexCount[i] = numVerts - verts;
+ break;
+ }
+ }
+ },
+
+ hasNormal: function() {
+ return (this._vf.normalOffset != 0) && !this._vf.sphericalNormals;
+ },
+
+ hasTexCoord: function() {
+ return (this._vf.texcoordOffset != 0);
+ },
+
+ hasColor: function() {
+ return (this._vf.colorOffset != 0);
+ },
+
+ getPositionPrecision : function() {
+ return this._vf.positionPrecision;
+ },
+
+ getNormalPrecision : function() {
+ return this._vf.normalPrecision;
+ },
+
+ getTexCoordPrecision : function() {
+ return this._vf.texcoordPrecision;
+ },
+
+ getColorPrecision : function() {
+ return this._vf.colorPrecision;
+ },
+
+ getAttributeStride : function() {
+ return this._vf.attributeStride;
+ },
+
+ getPositionOffset : function() {
+ return this._vf.positionOffset;
+ },
+
+ getNormalOffset : function() {
+ return this._vf.normalOffset;
+ },
+
+ getTexCoordOffset : function() {
+ return this._vf.texcoordOffset;
+ },
+
+ getColorOffset : function() {
+ return this._vf.colorOffset;
+ },
+
+ getBufferTypeStringFromByteCount: function(bytes) {
+ switch(bytes)
+ {
+ case 1:
+ return "Uint8";
+ case 2:
+ return "Uint16";
+ //case 4: //currently not supported by PopGeometry
+ // return "Float32";
+ default:
+ return 0;
+ }
+ },
+
+ getDataURLs : function() {
+ var urls = [];
+
+ for (var i = 0; i < this._cf.levels.nodes.length; ++i) {
+ urls.push(this._cf.levels.nodes[i].getSrc());
+ }
+
+ return urls;
+ },
+
+ getNumIndicesByLevel : function(lvl) {
+ return this._cf.levels.nodes[lvl].getNumIndices();
+ },
+
+ getNumLevels : function(lvl) {
+ return this._cf.levels.nodes.length;
+ },
+
+ getVertexDataBufferOffset : function(lvl) {
+ return this._cf.levels.nodes[lvl].getVertexDataBufferOffset();
+ },
+
+ getPrecisionMax: function(type) {
+ switch(this._vf[type])
+ {
+ //currently, only Uint8 and Uint16 are supported
+ //case "Int8":
+ // return 127.0;
+ case "Uint8":
+ return 255.0;
+ //case "Int16":
+ // return 32767.0;
+ case "Uint16":
+ return 65535.0;
+ //case "Int32":
+ //return 2147483647.0;
+ //case "Uint32":
+ //return 4294967295.0;
+ //case "Float32":
+ //case "Float64":
+ default:
+ return 1.0;
+ }
+ }
+ }
+ )
+);
+
+
+/** Static class members (needed for stats) */
+x3dom.nodeTypes.PopGeometry.ErrorToleranceFactor = 1;
+x3dom.nodeTypes.PopGeometry.PrecisionFactorOnMove = 1;
+x3dom.nodeTypes.PopGeometry.numRenderedVerts = 0;
+x3dom.nodeTypes.PopGeometry.numRenderedTris = 0;
+x3dom.nodeTypes.PopGeometry.numTotalVerts = 0;
+x3dom.nodeTypes.PopGeometry.numTotalTris = 0;
+
+/** Static LUT for LOD computation */
+x3dom.nodeTypes.PopGeometry.powLUT = [32768, 16384, 8192, 4096, 2048, 1024, 512, 256,
+ 128, 64, 32, 16, 8, 4, 2, 1];
+/** @namespace x3dom.nodeTypes */
+/*
+ * X3DOM JavaScript Library
+ * http://www.x3dom.org
+ *
+ * (C)2009 Fraunhofer IGD, Darmstadt, Germany
+ * Dual licensed under the MIT and GPL
+ */
+
+/* ### ImageGeometry ### */
+x3dom.registerNodeType(
+ "ImageGeometry",
+ "Geometry3D",
+ defineClass(x3dom.nodeTypes.X3DBinaryContainerGeometryNode,
+
+ /**
+ * Constructor for ImageGeometry
+ * @constructs x3dom.nodeTypes.ImageGeometry
+ * @x3d x.x
+ * @component Geometry3D
+ * @extends x3dom.nodeTypes.X3DBinaryContainerGeometryNode
+ * @param {Object} [ctx=null] - context object, containing initial settings like namespace
+ * @classdesc The image geometry node loads data stored in an image file.
+ */
+ function (ctx) {
+ x3dom.nodeTypes.ImageGeometry.superClass.call(this, ctx);
+
+
+ /**
+ *
+ * @var {x3dom.fields.SFVec2f} implicitMeshSize
+ * @memberof x3dom.nodeTypes.ImageGeometry
+ * @initvalue 256,256
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFVec2f(ctx, 'implicitMeshSize', 256, 256);
+
+ /**
+ * Specifies the number of color components.
+ * @var {x3dom.fields.SFInt32} numColorComponents
+ * @memberof x3dom.nodeTypes.ImageGeometry
+ * @initvalue 3
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFInt32(ctx, 'numColorComponents', 3);
+
+ /**
+ * Specifies the number of texture coordinate components.
+ * @var {x3dom.fields.SFInt32} numTexCoordComponents
+ * @memberof x3dom.nodeTypes.ImageGeometry
+ * @initvalue 2
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFInt32(ctx, 'numTexCoordComponents', 2);
+
+
+ /**
+ * Specifies the image file that contains the index data.
+ * @var {x3dom.fields.SFNode} index
+ * @memberof x3dom.nodeTypes.ImageGeometry
+ * @initvalue x3dom.nodeTypes.X3DTextureNode
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFNode('index', x3dom.nodeTypes.X3DTextureNode);
+
+ /**
+ * Specifies the image file that contains the coord data.
+ * @var {x3dom.fields.MFNode} coord
+ * @memberof x3dom.nodeTypes.ImageGeometry
+ * @initvalue x3dom.nodeTypes.X3DTextureNode
+ * @field x3dom
+ * @instance
+ */
+ this.addField_MFNode('coord', x3dom.nodeTypes.X3DTextureNode);
+
+ /**
+ * Specifies the image file that contains the normal data.
+ * @var {x3dom.fields.SFNode} normal
+ * @memberof x3dom.nodeTypes.ImageGeometry
+ * @initvalue x3dom.nodeTypes.X3DTextureNode
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFNode('normal', x3dom.nodeTypes.X3DTextureNode);
+
+ /**
+ * Specifies the image file that contains the texcoord data.
+ * @var {x3dom.fields.SFNode} texCoord
+ * @memberof x3dom.nodeTypes.ImageGeometry
+ * @initvalue x3dom.nodeTypes.X3DTextureNode
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFNode('texCoord', x3dom.nodeTypes.X3DTextureNode);
+
+ /**
+ * Specifies the image file that contains the color data.
+ * @var {x3dom.fields.SFNode} color
+ * @memberof x3dom.nodeTypes.ImageGeometry
+ * @initvalue x3dom.nodeTypes.X3DTextureNode
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFNode('color', x3dom.nodeTypes.X3DTextureNode);
+
+ this._mesh._numColComponents = this._vf.numColorComponents;
+ this._mesh._numTexComponents = this._vf.numTexCoordComponents;
+
+ if (this._vf.implicitMeshSize.y == 0)
+ this._vf.implicitMeshSize.y = this._vf.implicitMeshSize.x;
+
+ //TODO check if GPU-Version is supported (Flash, etc.)
+ //Dummy mesh generation only needed for GPU-Version
+ if (x3dom.caps.BACKEND == 'webgl' && x3dom.caps.MAX_VERTEX_TEXTURE_IMAGE_UNITS > 0) {
+
+ var geoCacheID = 'ImageGeometry_' + this._vf.implicitMeshSize.x + '_' + this._vf.implicitMeshSize.y;
+
+ if( this._vf.useGeoCache && x3dom.geoCache[geoCacheID] !== undefined )
+ {
+ //x3dom.debug.logInfo("Using ImageGeometry-Mesh from Cache");
+ this._mesh = x3dom.geoCache[geoCacheID];
+ }
+ else
+ {
+ for(var y=0; y<this._vf.implicitMeshSize.y; y++)
+ {
+ for(var x=0; x<this._vf.implicitMeshSize.x; x++)
+ {
+ this._mesh._positions[0].push(x / this._vf.implicitMeshSize.x,
+ y / this._vf.implicitMeshSize.y, 0);
+ }
+ }
+
+ //this._mesh._invalidate = true;
+ this._mesh._numFaces = this._mesh._indices[0].length / 3;
+ this._mesh._numCoords = this._mesh._positions[0].length / 3;
+
+ x3dom.geoCache[geoCacheID] = this._mesh;
+ }
+ }
+
+ // needed because mesh is shared due to cache
+ this._vol = new x3dom.fields.BoxVolume();
+
+ this._dirty = {
+ coord: true,
+ normal: true,
+ texCoord: true,
+ color: true,
+ index: true
+ };
+
+ },
+ {
+ setGeoDirty: function () {
+ this._dirty.coord = true;
+ this._dirty.normal = true;
+ this._dirty.texCoords = true;
+ this._dirty.color = true;
+ this._dirty.index = true;
+ },
+
+ unsetGeoDirty: function () {
+ this._dirty.coord = false;
+ this._dirty.normal = false;
+ this._dirty.texCoords = false;
+ this._dirty.color = false;
+ this._dirty.index = false;
+ },
+
+ nodeChanged: function()
+ {
+ Array.forEach(this._parentNodes, function (node) {
+ node._dirty.positions = true;
+ node._dirty.normals = true;
+ node._dirty.texcoords = true;
+ node._dirty.colors = true;
+ });
+ this._vol.invalidate();
+ },
+
+ fieldChanged: function(fieldName)
+ {
+ if (fieldName == "index" ||fieldName == "coord" || fieldName == "normal" ||
+ fieldName == "texCoord" || fieldName == "color") {
+ this._dirty[fieldName] = true;
+ this._vol.invalidate();
+ }
+ else if (fieldName == "implicitMeshSize") {
+ this._vol.invalidate();
+ }
+ },
+
+ getMin: function() {
+ var vol = this._vol;
+
+ if (!vol.isValid()) {
+ vol.setBoundsByCenterSize(this._vf.position, this._vf.size);
+ }
+
+ return vol.min;
+ },
+
+ getMax: function() {
+ var vol = this._vol;
+
+ if (!vol.isValid()) {
+ vol.setBoundsByCenterSize(this._vf.position, this._vf.size);
+ }
+
+ return vol.max;
+ },
+
+ getVolume: function() {
+ var vol = this._vol;
+
+ if (!vol.isValid()) {
+ vol.setBoundsByCenterSize(this._vf.position, this._vf.size);
+ }
+
+ return vol;
+ },
+
+ numCoordinateTextures: function()
+ {
+ return this._cf.coord.nodes.length;
+ },
+
+ getIndexTexture: function()
+ {
+ if(this._cf.index.node) {
+ this._cf.index.node._type = "IG_index";
+ return this._cf.index.node;
+ } else {
+ return null;
+ }
+ },
+
+ getIndexTextureURL: function()
+ {
+ if(this._cf.index.node) {
+ return this._cf.index.node._vf.url;
+ } else {
+ return null;
+ }
+ },
+
+ getCoordinateTexture: function(pos)
+ {
+ if(this._cf.coord.nodes[pos]) {
+ this._cf.coord.nodes[pos]._type = "IG_coords" + pos;
+ return this._cf.coord.nodes[pos];
+ } else {
+ return null;
+ }
+ },
+
+ getCoordinateTextureURL: function(pos)
+ {
+ if(this._cf.coord.nodes[pos]) {
+ return this._cf.coord.nodes[pos]._vf.url;
+ } else {
+ return null;
+ }
+ },
+
+ getCoordinateTextureURLs: function()
+ {
+ var urls = [];
+ for(var i=0; i<this._cf.coord.nodes.length; i++)
+ {
+ urls.push(this._cf.coord.nodes[i]._vf.url);
+ }
+ return urls;
+ },
+
+ getNormalTexture: function()
+ {
+ if(this._cf.normal.node) {
+ this._cf.normal.node._type = "IG_normals";
+ return this._cf.normal.node;
+ } else {
+ return null;
+ }
+ },
+
+ getNormalTextureURL: function()
+ {
+ if(this._cf.normal.node) {
+ return this._cf.normal.node._vf.url;
+ } else {
+ return null;
+ }
+ },
+
+ getTexCoordTexture: function()
+ {
+ if(this._cf.texCoord.node) {
+ this._cf.texCoord.node._type = "IG_texCoords";
+ return this._cf.texCoord.node;
+ } else {
+ return null;
+ }
+ },
+
+ getTexCoordTextureURL: function()
+ {
+ if(this._cf.texCoord.node) {
+ return this._cf.texCoord.node._vf.url;
+ } else {
+ return null;
+ }
+ },
+
+ getColorTexture: function()
+ {
+ if(this._cf.color.node) {
+ this._cf.color.node._type = "IG_colors";
+ return this._cf.color.node;
+ } else {
+ return null;
+ }
+ },
+
+ getColorTextureURL: function()
+ {
+ if(this._cf.color.node) {
+ return this._cf.color.node._vf.url;
+ } else {
+ return null;
+ }
+ },
+
+ getTextures: function()
+ {
+ var textures = [];
+
+ var index = this.getIndexTexture();
+ if(index) textures.push(index);
+
+ for(i=0; i<this.numCoordinateTextures(); i++) {
+ var coord = this.getCoordinateTexture(i);
+ if(coord) textures.push(coord);
+ }
+
+ var normal = this.getNormalTexture();
+ if(normal) textures.push(normal);
+
+ var texCoord = this.getTexCoordTexture();
+ if(texCoord) textures.push(texCoord);
+
+ var color = this.getColorTexture();
+ if(color) textures.push(color);
+
+ return textures;
+ }
+ }
+ )
+);
+/** @namespace x3dom.nodeTypes */
+/*
+ * X3DOM JavaScript Library
+ * http://www.x3dom.org
+ *
+ * (C)2009 Fraunhofer IGD, Darmstadt, Germany
+ * Dual licensed under the MIT and GPL
+ */
+
+/* ### IndexedFaceSet ### */
+x3dom.registerNodeType(
+ "IndexedFaceSet",
+ "Geometry3D",
+ defineClass(x3dom.nodeTypes.X3DComposedGeometryNode,
+
+ /**
+ * Constructor for IndexedFaceSet
+ * @constructs x3dom.nodeTypes.IndexedFaceSet
+ * @x3d 3.3
+ * @component Geometry3D
+ * @status experimental
+ * @extends x3dom.nodeTypes.X3DComposedGeometryNode
+ * @param {Object} [ctx=null] - context object, containing initial settings like namespace
+ * The IndexedFaceSet node represents a 3D shape formed by constructing faces (polygons) from vertices listed in the coord field.
+ */
+ function (ctx) {
+ x3dom.nodeTypes.IndexedFaceSet.superClass.call(this, ctx);
+
+
+ /**
+ * The creaseAngle field affects how default normals are generated.
+ * If the angle between the geometric normals of two adjacent faces is less than the crease angle, normals shall be calculated so that the faces are shaded smoothly across the edge; otherwise, normals shall be calculated so that a lighting discontinuity across the edge is produced.
+ * Crease angles shall be greater than or equal to 0.0 angle base units.
+ * @var {x3dom.fields.SFFloat} creaseAngle
+ * @memberof x3dom.nodeTypes.IndexedFaceSet
+ * @initvalue 0
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFFloat(ctx, 'creaseAngle', 0); // TODO
+
+ /**
+ * The convex field indicates whether all polygons in the shape are convex (TRUE).
+ * A polygon is convex if it is planar, does not intersect itself, and all of the interior angles at its vertices are less than 180 degrees.
+ * @var {x3dom.fields.SFBool} convex
+ * @memberof x3dom.nodeTypes.IndexedFaceSet
+ * @initvalue true
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFBool(ctx, 'convex', true);
+
+
+ /**
+ * The index data for the coord data.
+ * @var {x3dom.fields.MFInt32} coordIndex
+ * @memberof x3dom.nodeTypes.IndexedFaceSet
+ * @initvalue []
+ * @field x3dom
+ * @instance
+ */
+ this.addField_MFInt32(ctx, 'coordIndex', []);
+
+ /**
+ * The index data for the normal data.
+ * @var {x3dom.fields.MFInt32} normalIndex
+ * @memberof x3dom.nodeTypes.IndexedFaceSet
+ * @initvalue []
+ * @field x3dom
+ * @instance
+ */
+ this.addField_MFInt32(ctx, 'normalIndex', []);
+
+ /**
+ * The index data for the color data.
+ * @var {x3dom.fields.MFInt32} colorIndex
+ * @memberof x3dom.nodeTypes.IndexedFaceSet
+ * @initvalue []
+ * @field x3dom
+ * @instance
+ */
+ this.addField_MFInt32(ctx, 'colorIndex', []);
+
+ /**
+ * The index data for the texcoord data.
+ * @var {x3dom.fields.MFInt32} texCoordIndex
+ * @memberof x3dom.nodeTypes.IndexedFaceSet
+ * @initvalue []
+ * @field x3dom
+ * @instance
+ */
+ this.addField_MFInt32(ctx, 'texCoordIndex', []);
+
+ },
+ {
+ nodeChanged: function()
+ {
+ var time0 = new Date().getTime();
+
+ this.handleAttribs();
+
+ var indexes = this._vf.coordIndex;
+
+ if (indexes.length && indexes[indexes.length-1] != -1)
+ {
+ indexes.push(-1);
+ x3dom.debug.logWarning('Last index value should be -1.');
+ }
+
+ var normalInd = this._vf.normalIndex;
+ var texCoordInd = this._vf.texCoordIndex;
+ var colorInd = this._vf.colorIndex;
+
+ var hasNormal = false, hasNormalInd = false;
+ var hasTexCoord = false, hasTexCoordInd = false;
+ var hasColor = false, hasColorInd = false;
+
+ var colPerVert = this._vf.colorPerVertex;
+ var normPerVert = this._vf.normalPerVertex;
+
+ if (normalInd.length > 0)
+ {
+ hasNormalInd = true;
+ }
+ if (texCoordInd.length > 0)
+ {
+ hasTexCoordInd = true;
+ }
+ if (colorInd.length > 0)
+ {
+ hasColorInd = true;
+ }
+
+ var positions, normals, texCoords, colors;
+
+ var coordNode = this._cf.coord.node;
+ x3dom.debug.assert(coordNode);
+ positions = coordNode.getPoints();
+
+ var normalNode = this._cf.normal.node;
+ if (normalNode)
+ {
+ hasNormal = true;
+ normals = normalNode._vf.vector;
+ }
+ else {
+ hasNormal = false;
+ }
+
+ var texMode = "", numTexComponents = 2;
+ var texCoordNode = this._cf.texCoord.node;
+ if (x3dom.isa(texCoordNode, x3dom.nodeTypes.MultiTextureCoordinate)) {
+ if (texCoordNode._cf.texCoord.nodes.length)
+ texCoordNode = texCoordNode._cf.texCoord.nodes[0];
+ }
+ if (texCoordNode)
+ {
+ if (texCoordNode._vf.point) {
+ hasTexCoord = true;
+ texCoords = texCoordNode._vf.point;
+
+ if (x3dom.isa(texCoordNode, x3dom.nodeTypes.TextureCoordinate3D)) {
+ numTexComponents = 3;
+ }
+ }
+ else if (texCoordNode._vf.mode) {
+ texMode = texCoordNode._vf.mode;
+ }
+ }
+ else {
+ hasTexCoord = false;
+ }
+ this._mesh._numTexComponents = numTexComponents;
+
+ var numColComponents = 3;
+ var colorNode = this._cf.color.node;
+ if (colorNode)
+ {
+ hasColor = true;
+ colors = colorNode._vf.color;
+
+ if (x3dom.isa(colorNode, x3dom.nodeTypes.ColorRGBA)) {
+ numColComponents = 4;
+ }
+ }
+ else {
+ hasColor = false;
+ }
+ this._mesh._numColComponents = numColComponents;
+
+ this._mesh._indices[0] = [];
+ this._mesh._positions[0] = [];
+ this._mesh._normals[0] = [];
+ this._mesh._texCoords[0] = [];
+ this._mesh._colors[0] = [];
+
+ var i, j, t, cnt, faceCnt;
+ var p0, p1, p2, n0, n1, n2, t0, t1, t2, c0, c1, c2;
+
+ if ( (this._vf.creaseAngle <= x3dom.fields.Eps) || // FIXME; what to do for ipols?
+ (positions.length > x3dom.Utils.maxIndexableCoords) ||
+ (hasNormal && hasNormalInd) ||
+ (hasTexCoord && hasTexCoordInd) ||
+ (hasColor && hasColorInd) )
+ {
+ if (this._vf.creaseAngle <= x3dom.fields.Eps)
+ x3dom.debug.logWarning('Fallback to inefficient multi-index mode since creaseAngle=0.');
+
+ // Found MultiIndex Mesh
+ if(this._vf.convex) {
+ t = 0;
+ cnt = 0;
+ faceCnt = 0;
+ this._mesh._multiIndIndices = [];
+ this._mesh._posSize = positions.length;
+
+ for (i=0; i < indexes.length; ++i)
+ {
+ // Convert non-triangular polygons to a triangle fan
+ // (TODO: this assumes polygons are convex)
+ if (indexes[i] == -1) {
+ t = 0;
+ faceCnt++;
+ continue;
+ }
+
+ if (hasNormalInd) {
+ x3dom.debug.assert(normalInd[i] != -1);
+ }
+ if (hasTexCoordInd) {
+ x3dom.debug.assert(texCoordInd[i] != -1);
+ }
+ if (hasColorInd) {
+ x3dom.debug.assert(colorInd[i] != -1);
+ }
+
+ //TODO: OPTIMIZE but think about cache coherence regarding arrays!!!
+ switch (t)
+ {
+ case 0:
+ p0 = +indexes[i];
+ if (hasNormalInd && normPerVert) { n0 = +normalInd[i]; }
+ else if (hasNormalInd && !normPerVert) { n0 = +normalInd[faceCnt]; }
+ else if (normPerVert) { n0 = p0; }
+ else { n0 = faceCnt; }
+
+ if (hasTexCoordInd) { t0 = +texCoordInd[i]; }
+ else { t0 = p0; }
+ if (hasColorInd && colPerVert) { c0 = +colorInd[i]; }
+ else if (hasColorInd && !colPerVert) { c0 = +colorInd[faceCnt]; }
+ else if (colPerVert) { c0 = p0; }
+ else { c0 = faceCnt; }
+ t = 1;
+ break;
+ case 1:
+ p1 = +indexes[i];
+ if (hasNormalInd && normPerVert) { n1 = +normalInd[i]; }
+ else if (hasNormalInd && !normPerVert) { n1 = +normalInd[faceCnt]; }
+ else if (normPerVert) { n1 = p1; }
+ else { n1 = faceCnt; }
+
+ if (hasTexCoordInd) { t1 = +texCoordInd[i]; }
+ else { t1 = p1; }
+ if (hasColorInd && colPerVert) { c1 = +colorInd[i]; }
+ else if (hasColorInd && !colPerVert) { c1 = +colorInd[faceCnt]; }
+ else if (colPerVert) { c1 = p1; }
+ else { c1 = faceCnt; }
+ t = 2;
+ break;
+ case 2:
+ p2 = +indexes[i];
+ if (hasNormalInd && normPerVert) { n2 = +normalInd[i]; }
+ else if (hasNormalInd && !normPerVert) { n2 = +normalInd[faceCnt]; }
+ else if (normPerVert) { n2 = p2; }
+ else { n2 = faceCnt; }
+
+ if (hasTexCoordInd) { t2 = +texCoordInd[i]; }
+ else { t2 = p2; }
+ if (hasColorInd && colPerVert) { c2 = +colorInd[i]; }
+ else if (hasColorInd && !colPerVert) { c2 = +colorInd[faceCnt]; }
+ else if (colPerVert) { c2 = p2; }
+ else { c2 = faceCnt; }
+ t = 3;
+
+ //this._mesh._indices[0].push(cnt++, cnt++, cnt++);
+
+ this._mesh._positions[0].push(positions[p0].x);
+ this._mesh._positions[0].push(positions[p0].y);
+ this._mesh._positions[0].push(positions[p0].z);
+ this._mesh._positions[0].push(positions[p1].x);
+ this._mesh._positions[0].push(positions[p1].y);
+ this._mesh._positions[0].push(positions[p1].z);
+ this._mesh._positions[0].push(positions[p2].x);
+ this._mesh._positions[0].push(positions[p2].y);
+ this._mesh._positions[0].push(positions[p2].z);
+
+ if (hasNormal) {
+ this._mesh._normals[0].push(normals[n0].x);
+ this._mesh._normals[0].push(normals[n0].y);
+ this._mesh._normals[0].push(normals[n0].z);
+ this._mesh._normals[0].push(normals[n1].x);
+ this._mesh._normals[0].push(normals[n1].y);
+ this._mesh._normals[0].push(normals[n1].z);
+ this._mesh._normals[0].push(normals[n2].x);
+ this._mesh._normals[0].push(normals[n2].y);
+ this._mesh._normals[0].push(normals[n2].z);
+ }
+ //else {
+ this._mesh._multiIndIndices.push(p0, p1, p2);
+ //this._mesh._multiIndIndices.push(cnt-3, cnt-2, cnt-1);
+ //}
+
+ if (hasColor) {
+ this._mesh._colors[0].push(colors[c0].r);
+ this._mesh._colors[0].push(colors[c0].g);
+ this._mesh._colors[0].push(colors[c0].b);
+ if (numColComponents === 4) {
+ this._mesh._colors[0].push(colors[c0].a);
+ }
+ this._mesh._colors[0].push(colors[c1].r);
+ this._mesh._colors[0].push(colors[c1].g);
+ this._mesh._colors[0].push(colors[c1].b);
+ if (numColComponents === 4) {
+ this._mesh._colors[0].push(colors[c1].a);
+ }
+ this._mesh._colors[0].push(colors[c2].r);
+ this._mesh._colors[0].push(colors[c2].g);
+ this._mesh._colors[0].push(colors[c2].b);
+ if (numColComponents === 4) {
+ this._mesh._colors[0].push(colors[c2].a);
+ }
+ }
+
+ if (hasTexCoord) {
+ this._mesh._texCoords[0].push(texCoords[t0].x);
+ this._mesh._texCoords[0].push(texCoords[t0].y);
+ if (numTexComponents === 3) {
+ this._mesh._texCoords[0].push(texCoords[t0].z);
+ }
+ this._mesh._texCoords[0].push(texCoords[t1].x);
+ this._mesh._texCoords[0].push(texCoords[t1].y);
+ if (numTexComponents === 3) {
+ this._mesh._texCoords[0].push(texCoords[t1].z);
+ }
+ this._mesh._texCoords[0].push(texCoords[t2].x);
+ this._mesh._texCoords[0].push(texCoords[t2].y);
+ if (numTexComponents === 3) {
+ this._mesh._texCoords[0].push(texCoords[t2].z);
+ }
+ }
+
+ //faceCnt++;
+ break;
+ case 3:
+ p1 = p2;
+ t1 = t2;
+ if (normPerVert) {
+ n1 = n2;
+ }
+ if (colPerVert) {
+ c1 = c2;
+ }
+ p2 = +indexes[i];
+
+ if (hasNormalInd && normPerVert) {
+ n2 = +normalInd[i];
+ } else if (hasNormalInd && !normPerVert) {
+ /*n2 = +normalInd[faceCnt];*/
+ } else if (normPerVert) {
+ n2 = p2;
+ } else {
+ n2 = faceCnt;
+ }
+
+ if (hasTexCoordInd) {
+ t2 = +texCoordInd[i];
+ } else {
+ t2 = p2;
+ }
+
+ if (hasColorInd && colPerVert) {
+ c2 = +colorInd[i];
+ } else if (hasColorInd && !colPerVert) {
+ /*c2 = +colorInd[faceCnt];*/
+ } else if (colPerVert) {
+ c2 = p2;
+ } else {
+ c2 = faceCnt;
+ }
+
+ //this._mesh._indices[0].push(cnt++, cnt++, cnt++);
+
+ this._mesh._positions[0].push(positions[p0].x);
+ this._mesh._positions[0].push(positions[p0].y);
+ this._mesh._positions[0].push(positions[p0].z);
+ this._mesh._positions[0].push(positions[p1].x);
+ this._mesh._positions[0].push(positions[p1].y);
+ this._mesh._positions[0].push(positions[p1].z);
+ this._mesh._positions[0].push(positions[p2].x);
+ this._mesh._positions[0].push(positions[p2].y);
+ this._mesh._positions[0].push(positions[p2].z);
+
+ if (hasNormal) {
+ this._mesh._normals[0].push(normals[n0].x);
+ this._mesh._normals[0].push(normals[n0].y);
+ this._mesh._normals[0].push(normals[n0].z);
+ this._mesh._normals[0].push(normals[n1].x);
+ this._mesh._normals[0].push(normals[n1].y);
+ this._mesh._normals[0].push(normals[n1].z);
+ this._mesh._normals[0].push(normals[n2].x);
+ this._mesh._normals[0].push(normals[n2].y);
+ this._mesh._normals[0].push(normals[n2].z);
+ }
+ //else {
+ this._mesh._multiIndIndices.push(p0, p1, p2);
+ //this._mesh._multiIndIndices.push(cnt-3, cnt-2, cnt-1);
+ //}
+
+ if (hasColor) {
+ this._mesh._colors[0].push(colors[c0].r);
+ this._mesh._colors[0].push(colors[c0].g);
+ this._mesh._colors[0].push(colors[c0].b);
+ if (numColComponents === 4) {
+ this._mesh._colors[0].push(colors[c0].a);
+ }
+ this._mesh._colors[0].push(colors[c1].r);
+ this._mesh._colors[0].push(colors[c1].g);
+ this._mesh._colors[0].push(colors[c1].b);
+ if (numColComponents === 4) {
+ this._mesh._colors[0].push(colors[c1].a);
+ }
+ this._mesh._colors[0].push(colors[c2].r);
+ this._mesh._colors[0].push(colors[c2].g);
+ this._mesh._colors[0].push(colors[c2].b);
+ if (numColComponents === 4) {
+ this._mesh._colors[0].push(colors[c2].a);
+ }
+ }
+
+ if (hasTexCoord) {
+ this._mesh._texCoords[0].push(texCoords[t0].x);
+ this._mesh._texCoords[0].push(texCoords[t0].y);
+ if (numTexComponents === 3) {
+ this._mesh._texCoords[0].push(texCoords[t0].z);
+ }
+ this._mesh._texCoords[0].push(texCoords[t1].x);
+ this._mesh._texCoords[0].push(texCoords[t1].y);
+ if (numTexComponents === 3) {
+ this._mesh._texCoords[0].push(texCoords[t1].z);
+ }
+ this._mesh._texCoords[0].push(texCoords[t2].x);
+ this._mesh._texCoords[0].push(texCoords[t2].y);
+ if (numTexComponents === 3) {
+ this._mesh._texCoords[0].push(texCoords[t2].z);
+ }
+ }
+
+ //faceCnt++;
+ break;
+ default:
+ }
+ }
+ }
+ else {
+ var linklist = new x3dom.DoublyLinkedList();
+ var data = {};
+ cnt = 0; faceCnt = 0;
+
+ for (i = 0; i < indexes.length; ++i)
+ {
+ if (indexes[i] == -1) {
+ var multi_index_data = x3dom.EarClipping.getMultiIndexes(linklist);
+
+ for (j = 0; j < multi_index_data.indices.length; j++)
+ {
+ this._mesh._indices[0].push(cnt);
+ cnt++;
+
+ this._mesh._positions[0].push(multi_index_data.point[j].x,
+ multi_index_data.point[j].y,
+ multi_index_data.point[j].z);
+ if (hasNormal) {
+ this._mesh._normals[0].push(multi_index_data.normals[j].x,
+ multi_index_data.normals[j].y,
+ multi_index_data.normals[j].z);
+ }
+ if (hasColor) {
+ this._mesh._colors[0].push(multi_index_data.colors[j].r,
+ multi_index_data.colors[j].g,
+ multi_index_data.colors[j].b);
+ if (numColComponents === 4) {
+ this._mesh._colors[0].push(multi_index_data.colors[j].a);
+ }
+ }
+ if (hasTexCoord) {
+ this._mesh._texCoords[0].push(multi_index_data.texCoords[j].x,
+ multi_index_data.texCoords[j].y);
+ if (numTexComponents === 3) {
+ this._mesh._texCoords[0].push(multi_index_data.texCoords[j].z);
+ }
+ }
+ }
+
+ linklist = new x3dom.DoublyLinkedList();
+ faceCnt++;
+ continue;
+ }
+
+ if (hasNormal) {
+ if (hasNormalInd && normPerVert) {
+ data.normals = normals[normalInd[i]];
+ } else if (hasNormalInd && !normPerVert) {
+ data.normals = normals[normalInd[faceCnt]];
+ } else {
+ data.normals = normals[indexes[i]];
+ }
+ }
+
+ if (hasColor) {
+ if (hasColorInd && colPerVert) {
+ data.colors = colors[colorInd[i]];
+ } else if (hasColorInd && !colPerVert) {
+ data.colors = colors[colorInd[faceCnt]];
+ } else if (colPerVert) {
+ data.colors = colors[indexes[i]];
+ } else {
+ data.colors = colors[faceCnt];
+ }
+ }
+ if (hasTexCoord) {
+ if (hasTexCoordInd) {
+ data.texCoords = texCoords[texCoordInd[i]];
+ } else {
+ data.texCoords = texCoords[indexes[i]];
+ }
+ }
+
+ linklist.appendNode(new x3dom.DoublyLinkedList.ListNode(
+ positions[indexes[i]], indexes[i], data.normals, data.colors, data.texCoords));
+ }
+
+ this._mesh.splitMesh();
+ }
+
+ if (!hasNormal) {
+ this._mesh.calcNormals(this._vf.creaseAngle, this._vf.ccw);
+ }
+ if (!hasTexCoord) {
+ this._mesh.calcTexCoords(texMode);
+ }
+ } // if isMulti
+ else
+ {
+ t = 0;
+ if (this._vf.convex) {
+ for (i = 0; i < indexes.length; ++i)
+ {
+ // Convert non-triangular polygons to a triangle fan
+ if (indexes[i] == -1) {
+ t = 0;
+ continue;
+ }
+
+ switch (t) {
+ case 0: n0 = +indexes[i]; t = 1; break;
+ case 1: n1 = +indexes[i]; t = 2; break;
+ case 2: n2 = +indexes[i]; t = 3; this._mesh._indices[0].push(n0, n1, n2); break;
+ case 3: n1 = n2; n2 = +indexes[i]; this._mesh._indices[0].push(n0, n1, n2); break;
+ }
+
+ }
+ }
+ else {
+ // Convert non-triangular convex polygons to a triangle fan
+ linklist = new x3dom.DoublyLinkedList();
+ for (i = 0; i < indexes.length; ++i)
+ {
+ if (indexes[i] == -1) {
+ var linklist_indices = x3dom.EarClipping.getIndexes(linklist);
+
+ for (j = 0; j < linklist_indices.length; j++) {
+ this._mesh._indices[0].push(linklist_indices[j]);
+ }
+ linklist = new x3dom.DoublyLinkedList();
+ continue;
+ }
+
+ linklist.appendNode(new x3dom.DoublyLinkedList.ListNode(positions[indexes[i]], indexes[i]));
+ }
+ }
+
+ this._mesh._positions[0] = positions.toGL();
+
+ if (hasNormal) {
+ this._mesh._normals[0] = normals.toGL();
+ }
+ else {
+ this._mesh.calcNormals(this._vf.creaseAngle, this._vf.ccw);
+ }
+ if (hasTexCoord) {
+ this._mesh._texCoords[0] = texCoords.toGL();
+ this._mesh._numTexComponents = numTexComponents;
+ }
+ else {
+ this._mesh.calcTexCoords(texMode);
+ }
+ if (hasColor) {
+ this._mesh._colors[0] = colors.toGL();
+ this._mesh._numColComponents = numColComponents;
+ }
+ }
+
+ this.invalidateVolume();
+
+ this._mesh._numFaces = 0;
+ this._mesh._numCoords = 0;
+
+ for (i=0; i<this._mesh._positions.length; i++) {
+ var indexLength = this._mesh._indices[i].length;
+ var numCoords = this._mesh._positions[i].length / 3;
+ this._mesh._numCoords += numCoords;
+ if (indexLength > 0)
+ this._mesh._numFaces += indexLength / 3;
+ else
+ this._mesh._numFaces += numCoords / 3;
+ }
+
+ //var time1 = new Date().getTime() - time0;
+ //x3dom.debug.logInfo("Mesh load time: " + time1 + " ms");
+ },
+
+ fieldChanged: function(fieldName)
+ {
+ if (fieldName != "coord" && fieldName != "normal" &&
+ fieldName != "texCoord" && fieldName != "color" &&
+ fieldName != "coordIndex")
+ {
+ x3dom.debug.logWarning("IndexedFaceSet: fieldChanged for " +
+ fieldName + " not yet implemented!");
+ return;
+ }
+
+ var pnts = this._cf.coord.node._vf.point;
+ var n = pnts.length;
+
+ var texCoordNode = this._cf.texCoord.node;
+ if (x3dom.isa(texCoordNode, x3dom.nodeTypes.MultiTextureCoordinate)) {
+ if (texCoordNode._cf.texCoord.nodes.length)
+ texCoordNode = texCoordNode._cf.texCoord.nodes[0];
+ }
+
+ if (((this._vf.creaseAngle <= x3dom.fields.Eps) || (n > x3dom.Utils.maxIndexableCoords) ||
+ (this._vf.normalIndex.length > 0 && this._cf.normal.node) ||
+ (this._vf.texCoordIndex.length > 0 && texCoordNode) ||
+ (this._vf.colorIndex.length > 0 && this._cf.color.node)) && this._mesh._multiIndIndices)
+ {
+ var needNormals = !this._cf.normal.node && this._vf.normalUpdateMode.toLowerCase() != 'none';
+
+ n = this._mesh._multiIndIndices.length;
+
+ this._mesh._positions[0] = [];
+ this._mesh._indices[0] =[];
+
+ // special coordinate interpolator handler
+ if (fieldName == "coord" && n)
+ {
+ if (needNormals) {
+ this._mesh._normals[0] = [];
+ }
+
+ for (i=0; i<n; i+=3) {
+ var ind0 = this._mesh._multiIndIndices[i ];
+ var ind1 = this._mesh._multiIndIndices[i+1];
+ var ind2 = this._mesh._multiIndIndices[i+2];
+
+ var pos0 = pnts[ind0];
+ var pos1 = pnts[ind1];
+ var pos2 = pnts[ind2];
+
+ this._mesh._positions[0].push(pos0.x, pos0.y, pos0.z);
+ this._mesh._positions[0].push(pos1.x, pos1.y, pos1.z);
+ this._mesh._positions[0].push(pos2.x, pos2.y, pos2.z);
+
+ if (needNormals) {
+ var a = pos0.subtract(pos1);
+ var b = pos1.subtract(pos2);
+
+ var norm = a.cross(b).normalize();
+ if (!this._vf.ccw)
+ norm = norm.negate();
+
+ this._mesh._normals[0].push(norm.x, norm.y, norm.z);
+ this._mesh._normals[0].push(norm.x, norm.y, norm.z);
+ this._mesh._normals[0].push(norm.x, norm.y, norm.z);
+ }
+ }
+
+ this.invalidateVolume();
+
+ Array.forEach(this._parentNodes, function (node) {
+ node._dirty.positions = true;
+ if (needNormals)
+ node._dirty.normals = true;
+ });
+
+ return;
+ }
+
+ // TODO; optimize this very slow and brute force code, at least for creaseAngle=0 case!
+ this._mesh._normals[0] = [];
+ this._mesh._texCoords[0] =[];
+ this._mesh._colors[0] = [];
+
+ var indexes = this._vf.coordIndex;
+ var normalInd = this._vf.normalIndex;
+ var texCoordInd = this._vf.texCoordIndex;
+ var colorInd = this._vf.colorIndex;
+ var hasNormal = false, hasNormalInd = false;
+ var hasTexCoord = false, hasTexCoordInd = false;
+ var hasColor = false, hasColorInd = false;
+
+ var colPerVert = this._vf.colorPerVertex;
+ var normPerVert = this._vf.normalPerVertex;
+
+ if (normalInd.length > 0)
+ {
+ hasNormalInd = true;
+ }
+ if (texCoordInd.length > 0)
+ {
+ hasTexCoordInd = true;
+ }
+ if (colorInd.length > 0)
+ {
+ hasColorInd = true;
+ }
+
+ var positions, normals, texCoords, colors;
+
+ var coordNode = this._cf.coord.node;
+ x3dom.debug.assert(coordNode);
+ positions = coordNode.getPoints();
+
+ var normalNode = this._cf.normal.node;
+ if (normalNode)
+ {
+ hasNormal = true;
+ normals = normalNode._vf.vector;
+ }
+ else {
+ hasNormal = false;
+ }
+
+ var texMode = "", numTexComponents = 2;
+ texCoordNode = this._cf.texCoord.node;
+ if (x3dom.isa(texCoordNode, x3dom.nodeTypes.MultiTextureCoordinate)) {
+ if (texCoordNode._cf.texCoord.nodes.length)
+ texCoordNode = texCoordNode._cf.texCoord.nodes[0];
+ }
+ if (texCoordNode)
+ {
+ if (texCoordNode._vf.point) {
+ hasTexCoord = true;
+ texCoords = texCoordNode._vf.point;
+
+ if (x3dom.isa(texCoordNode, x3dom.nodeTypes.TextureCoordinate3D)) {
+ numTexComponents = 3;
+ }
+ }
+ else if (texCoordNode._vf.mode) {
+ texMode = texCoordNode._vf.mode;
+ }
+ }
+ else {
+ hasTexCoord = false;
+ }
+ this._mesh._numTexComponents = numTexComponents;
+
+ var numColComponents = 3;
+ var colorNode = this._cf.color.node;
+ if (colorNode)
+ {
+ hasColor = true;
+ colors = colorNode._vf.color;
+
+ if (x3dom.isa(colorNode, x3dom.nodeTypes.ColorRGBA)) {
+ numColComponents = 4;
+ }
+ }
+ else {
+ hasColor = false;
+ }
+ this._mesh._numColComponents = numColComponents;
+
+ var i, j, t, cnt, faceCnt;
+ var p0, p1, p2, n0, n1, n2, t0, t1, t2, c0, c1, c2;
+
+ if(this._vf.convex) {
+ t = 0;
+ cnt = 0;
+ faceCnt = 0;
+ this._mesh._multiIndIndices = [];
+ this._mesh._posSize = positions.length;
+
+ for (i=0; i < indexes.length; ++i)
+ {
+ if (indexes[i] == -1) {
+ t = 0;
+ faceCnt++;
+ continue;
+ }
+
+ if (hasNormalInd) {
+ x3dom.debug.assert(normalInd[i] != -1);
+ }
+ if (hasTexCoordInd) {
+ x3dom.debug.assert(texCoordInd[i] != -1);
+ }
+ if (hasColorInd) {
+ x3dom.debug.assert(colorInd[i] != -1);
+ }
+
+ switch (t)
+ {
+ case 0:
+ p0 = +indexes[i];
+ if (hasNormalInd && normPerVert) { n0 = +normalInd[i]; }
+ else if (hasNormalInd && !normPerVert) { n0 = +normalInd[faceCnt]; }
+ else if (normPerVert) { n0 = p0; }
+ else { n0 = faceCnt; }
+
+ if (hasTexCoordInd) { t0 = +texCoordInd[i]; }
+ else { t0 = p0; }
+ if (hasColorInd && colPerVert) { c0 = +colorInd[i]; }
+ else if (hasColorInd && !colPerVert) { c0 = +colorInd[faceCnt]; }
+ else if (colPerVert) { c0 = p0; }
+ else { c0 = faceCnt; }
+ t = 1;
+ break;
+ case 1:
+ p1 = +indexes[i];
+ if (hasNormalInd && normPerVert) { n1 = +normalInd[i]; }
+ else if (hasNormalInd && !normPerVert) { n1 = +normalInd[faceCnt]; }
+ else if (normPerVert) { n1 = p1; }
+ else { n1 = faceCnt; }
+
+ if (hasTexCoordInd) { t1 = +texCoordInd[i]; }
+ else { t1 = p1; }
+ if (hasColorInd && colPerVert) { c1 = +colorInd[i]; }
+ else if (hasColorInd && !colPerVert) { c1 = +colorInd[faceCnt]; }
+ else if (colPerVert) { c1 = p1; }
+ else { c1 = faceCnt; }
+ t = 2;
+ break;
+ case 2:
+ p2 = +indexes[i];
+ if (hasNormalInd && normPerVert) { n2 = +normalInd[i]; }
+ else if (hasNormalInd && !normPerVert) { n2 = +normalInd[faceCnt]; }
+ else if (normPerVert) { n2 = p2; }
+ else { n2 = faceCnt; }
+
+ if (hasTexCoordInd) { t2 = +texCoordInd[i]; }
+ else { t2 = p2; }
+ if (hasColorInd && colPerVert) { c2 = +colorInd[i]; }
+ else if (hasColorInd && !colPerVert) { c2 = +colorInd[faceCnt]; }
+ else if (colPerVert) { c2 = p2; }
+ else { c2 = faceCnt; }
+ t = 3;
+
+ //this._mesh._indices[0].push(cnt++, cnt++, cnt++);
+
+ this._mesh._positions[0].push(positions[p0].x);
+ this._mesh._positions[0].push(positions[p0].y);
+ this._mesh._positions[0].push(positions[p0].z);
+ this._mesh._positions[0].push(positions[p1].x);
+ this._mesh._positions[0].push(positions[p1].y);
+ this._mesh._positions[0].push(positions[p1].z);
+ this._mesh._positions[0].push(positions[p2].x);
+ this._mesh._positions[0].push(positions[p2].y);
+ this._mesh._positions[0].push(positions[p2].z);
+
+ if (hasNormal) {
+ this._mesh._normals[0].push(normals[n0].x);
+ this._mesh._normals[0].push(normals[n0].y);
+ this._mesh._normals[0].push(normals[n0].z);
+ this._mesh._normals[0].push(normals[n1].x);
+ this._mesh._normals[0].push(normals[n1].y);
+ this._mesh._normals[0].push(normals[n1].z);
+ this._mesh._normals[0].push(normals[n2].x);
+ this._mesh._normals[0].push(normals[n2].y);
+ this._mesh._normals[0].push(normals[n2].z);
+ }
+ //else {
+ this._mesh._multiIndIndices.push(p0, p1, p2);
+ //}
+
+ if (hasColor) {
+ this._mesh._colors[0].push(colors[c0].r);
+ this._mesh._colors[0].push(colors[c0].g);
+ this._mesh._colors[0].push(colors[c0].b);
+ if (numColComponents === 4) {
+ this._mesh._colors[0].push(colors[c0].a);
+ }
+ this._mesh._colors[0].push(colors[c1].r);
+ this._mesh._colors[0].push(colors[c1].g);
+ this._mesh._colors[0].push(colors[c1].b);
+ if (numColComponents === 4) {
+ this._mesh._colors[0].push(colors[c1].a);
+ }
+ this._mesh._colors[0].push(colors[c2].r);
+ this._mesh._colors[0].push(colors[c2].g);
+ this._mesh._colors[0].push(colors[c2].b);
+ if (numColComponents === 4) {
+ this._mesh._colors[0].push(colors[c2].a);
+ }
+ }
+
+ if (hasTexCoord) {
+ this._mesh._texCoords[0].push(texCoords[t0].x);
+ this._mesh._texCoords[0].push(texCoords[t0].y);
+ if (numTexComponents === 3) {
+ this._mesh._texCoords[0].push(texCoords[t0].z);
+ }
+ this._mesh._texCoords[0].push(texCoords[t1].x);
+ this._mesh._texCoords[0].push(texCoords[t1].y);
+ if (numTexComponents === 3) {
+ this._mesh._texCoords[0].push(texCoords[t1].z);
+ }
+ this._mesh._texCoords[0].push(texCoords[t2].x);
+ this._mesh._texCoords[0].push(texCoords[t2].y);
+ if (numTexComponents === 3) {
+ this._mesh._texCoords[0].push(texCoords[t2].z);
+ }
+ }
+
+ //faceCnt++;
+ break;
+ case 3:
+ p1 = p2;
+ t1 = t2;
+ if (normPerVert) {
+ n1 = n2;
+ }
+ if (colPerVert) {
+ c1 = c2;
+ }
+ p2 = +indexes[i];
+
+ if (hasNormalInd && normPerVert) {
+ n2 = +normalInd[i];
+ } else if (hasNormalInd && !normPerVert) {
+ /*n2 = +normalInd[faceCnt];*/
+ } else if (normPerVert) {
+ n2 = p2;
+ } else {
+ n2 = faceCnt;
+ }
+
+ if (hasTexCoordInd) {
+ t2 = +texCoordInd[i];
+ } else {
+ t2 = p2;
+ }
+
+ if (hasColorInd && colPerVert) {
+ c2 = +colorInd[i];
+ } else if (hasColorInd && !colPerVert) {
+ /*c2 = +colorInd[faceCnt];*/
+ } else if (colPerVert) {
+ c2 = p2;
+ } else {
+ c2 = faceCnt;
+ }
+
+ //this._mesh._indices[0].push(cnt++, cnt++, cnt++);
+
+ this._mesh._positions[0].push(positions[p0].x);
+ this._mesh._positions[0].push(positions[p0].y);
+ this._mesh._positions[0].push(positions[p0].z);
+ this._mesh._positions[0].push(positions[p1].x);
+ this._mesh._positions[0].push(positions[p1].y);
+ this._mesh._positions[0].push(positions[p1].z);
+ this._mesh._positions[0].push(positions[p2].x);
+ this._mesh._positions[0].push(positions[p2].y);
+ this._mesh._positions[0].push(positions[p2].z);
+
+ if (hasNormal) {
+ this._mesh._normals[0].push(normals[n0].x);
+ this._mesh._normals[0].push(normals[n0].y);
+ this._mesh._normals[0].push(normals[n0].z);
+ this._mesh._normals[0].push(normals[n1].x);
+ this._mesh._normals[0].push(normals[n1].y);
+ this._mesh._normals[0].push(normals[n1].z);
+ this._mesh._normals[0].push(normals[n2].x);
+ this._mesh._normals[0].push(normals[n2].y);
+ this._mesh._normals[0].push(normals[n2].z);
+ }
+ //else {
+ this._mesh._multiIndIndices.push(p0, p1, p2);
+ //}
+
+ if (hasColor) {
+ this._mesh._colors[0].push(colors[c0].r);
+ this._mesh._colors[0].push(colors[c0].g);
+ this._mesh._colors[0].push(colors[c0].b);
+ if (numColComponents === 4) {
+ this._mesh._colors[0].push(colors[c0].a);
+ }
+ this._mesh._colors[0].push(colors[c1].r);
+ this._mesh._colors[0].push(colors[c1].g);
+ this._mesh._colors[0].push(colors[c1].b);
+ if (numColComponents === 4) {
+ this._mesh._colors[0].push(colors[c1].a);
+ }
+ this._mesh._colors[0].push(colors[c2].r);
+ this._mesh._colors[0].push(colors[c2].g);
+ this._mesh._colors[0].push(colors[c2].b);
+ if (numColComponents === 4) {
+ this._mesh._colors[0].push(colors[c2].a);
+ }
+ }
+
+ if (hasTexCoord) {
+ this._mesh._texCoords[0].push(texCoords[t0].x);
+ this._mesh._texCoords[0].push(texCoords[t0].y);
+ if (numTexComponents === 3) {
+ this._mesh._texCoords[0].push(texCoords[t0].z);
+ }
+ this._mesh._texCoords[0].push(texCoords[t1].x);
+ this._mesh._texCoords[0].push(texCoords[t1].y);
+ if (numTexComponents === 3) {
+ this._mesh._texCoords[0].push(texCoords[t1].z);
+ }
+ this._mesh._texCoords[0].push(texCoords[t2].x);
+ this._mesh._texCoords[0].push(texCoords[t2].y);
+ if (numTexComponents === 3) {
+ this._mesh._texCoords[0].push(texCoords[t2].z);
+ }
+ }
+
+ //faceCnt++;
+ break;
+ default:
+ }
+ }
+ }
+ else {
+ var linklist = new x3dom.DoublyLinkedList();
+ var data = {};
+ cnt = 0; faceCnt = 0;
+
+ for (i = 0; i < indexes.length; ++i)
+ {
+ if (indexes[i] == -1) {
+ var multi_index_data = x3dom.EarClipping.getMultiIndexes(linklist);
+
+ for (j = 0; j < multi_index_data.indices.length; j++)
+ {
+ this._mesh._indices[0].push(cnt);
+ cnt++;
+
+ this._mesh._positions[0].push(multi_index_data.point[j].x,
+ multi_index_data.point[j].y,
+ multi_index_data.point[j].z);
+ if (hasNormal) {
+ this._mesh._normals[0].push(multi_index_data.normals[j].x,
+ multi_index_data.normals[j].y,
+ multi_index_data.normals[j].z);
+ }
+ if (hasColor) {
+ this._mesh._colors[0].push(multi_index_data.colors[j].r,
+ multi_index_data.colors[j].g,
+ multi_index_data.colors[j].b);
+ if (numColComponents === 4) {
+ this._mesh._colors[0].push(multi_index_data.colors[j].a);
+ }
+ }
+ if (hasTexCoord) {
+ this._mesh._texCoords[0].push(multi_index_data.texCoords[j].x,
+ multi_index_data.texCoords[j].y);
+ if (numTexComponents === 3) {
+ this._mesh._texCoords[0].push(multi_index_data.texCoords[j].z);
+ }
+ }
+ }
+
+ linklist = new x3dom.DoublyLinkedList();
+ faceCnt++;
+ continue;
+ }
+
+ if (hasNormal) {
+ if (hasNormalInd && normPerVert) {
+ data.normals = normals[normalInd[i]];
+ } else if (hasNormalInd && !normPerVert) {
+ data.normals = normals[normalInd[faceCnt]];
+ } else {
+ data.normals = normals[indexes[i]];
+ }
+ }
+
+ if (hasColor) {
+ if (hasColorInd && colPerVert) {
+ data.colors = colors[colorInd[i]];
+ } else if (hasColorInd && !colPerVert) {
+ data.colors = colors[colorInd[faceCnt]];
+ } else {
+ data.colors = colors[indexes[i]];
+ }
+ }
+ if (hasTexCoord) {
+ if (hasTexCoordInd) {
+ data.texCoords = texCoords[texCoordInd[i]];
+ } else {
+ data.texCoords = texCoords[indexes[i]];
+ }
+ }
+
+ linklist.appendNode(new x3dom.DoublyLinkedList.ListNode(
+ positions[indexes[i]], indexes[i], data.normals, data.colors, data.texCoords));
+ }
+
+ this._mesh.splitMesh();
+ }
+
+ if (!hasNormal) {
+ this._mesh.calcNormals(this._vf.creaseAngle, this._vf.ccw);
+ }
+ if (!hasTexCoord) {
+ this._mesh.calcTexCoords(texMode);
+ }
+
+ this.invalidateVolume();
+
+ this._mesh._numFaces = 0;
+ this._mesh._numCoords = 0;
+
+ for (i=0; i<this._mesh._positions.length; i++) {
+ var indexLength = this._mesh._indices[i].length;
+ var numCoords = this._mesh._positions[i].length / 3;
+ this._mesh._numCoords += numCoords;
+ if (indexLength > 0)
+ this._mesh._numFaces += indexLength / 3;
+ else
+ this._mesh._numFaces += numCoords / 3;
+ }
+
+ Array.forEach(this._parentNodes, function (node) {
+ node.setGeoDirty();
+ });
+ }
+ else {
+ if (fieldName == "coord")
+ {
+ var needNormals = !this._cf.normal.node && this._vf.normalUpdateMode.toLowerCase() != 'none';
+
+ this._mesh._positions[0] = pnts.toGL();
+
+ if (needNormals) {
+ // position update usually also requires update of vertex normals
+ this._mesh.calcNormals(this._vf.creaseAngle, this._vf.ccw);
+ }
+
+ // tells the mesh that its bbox requires update
+ this.invalidateVolume();
+
+ Array.forEach(this._parentNodes, function (node) {
+ node._dirty.positions = true;
+ if (needNormals)
+ node._dirty.normals = true;
+ node.invalidateVolume();
+ });
+ }
+ else if (fieldName == "color")
+ {
+ pnts = this._cf.color.node._vf.color;
+
+ this._mesh._colors[0] = pnts.toGL();
+
+ Array.forEach(this._parentNodes, function (node) {
+ node._dirty.colors = true;
+ });
+ }
+ else if (fieldName == "normal")
+ {
+ pnts = this._cf.normal.node._vf.vector;
+
+ this._mesh._normals[0] = pnts.toGL();
+
+ Array.forEach(this._parentNodes, function (node) {
+ node._dirty.normals = true;
+ });
+ }
+ else if (fieldName == "texCoord")
+ {
+ texCoordNode = this._cf.texCoord.node;
+ if (x3dom.isa(texCoordNode, x3dom.nodeTypes.MultiTextureCoordinate)) {
+ if (texCoordNode._cf.texCoord.nodes.length)
+ texCoordNode = texCoordNode._cf.texCoord.nodes[0];
+ }
+ pnts = texCoordNode._vf.point;
+
+ this._mesh._texCoords[0] = pnts.toGL();
+
+ Array.forEach(this._parentNodes, function (node) {
+ node._dirty.texcoords = true;
+ });
+ }
+ else if (fieldName == "coordIndex")
+ {
+ needNormals = !this._cf.normal.node && this._vf.normalUpdateMode.toLowerCase() != 'none';
+
+ indexes = this._vf.coordIndex;
+ t = 0;
+ n = indexes.length;
+
+ this._mesh._indices[0] = [];
+
+ for (i = 0; i < n; ++i) {
+ if (indexes[i] == -1) {
+ t = 0;
+ }
+ else {
+ switch (t) {
+ case 0: p0 = +indexes[i]; t = 1; break;
+ case 1: p1 = +indexes[i]; t = 2; break;
+ case 2: p2 = +indexes[i]; t = 3; this._mesh._indices[0].push(p0, p1, p2); break;
+ case 3: p1 = p2; p2 = +indexes[i]; this._mesh._indices[0].push(p0, p1, p2); break;
+ }
+ }
+ }
+
+ if (needNormals) {
+ // index update usually also requires update of vertex normals
+ this._mesh.calcNormals(this._vf.creaseAngle, this._vf.ccw);
+ }
+
+ Array.forEach(this._parentNodes, function (node) {
+ node._dirty.indexes = true;
+ if (needNormals)
+ node._dirty.normals = true;
+ });
+ }
+ }
+ }
+ }
+ )
+);
+/** @namespace x3dom.nodeTypes */
+/*
+ * X3DOM JavaScript Library
+ * http://www.x3dom.org
+ *
+ * (C)2009 Fraunhofer IGD, Darmstadt, Germany
+ * Dual licensed under the MIT and GPL
+ */
+
+/* ### X3DTexture3DNode ### */
+x3dom.registerNodeType(
+ "X3DTexture3DNode",
+ "Texturing3D",
+ defineClass(x3dom.nodeTypes.X3DTextureNode,
+
+ /**
+ * Constructor for X3DTexture3DNode
+ * @constructs x3dom.nodeTypes.X3DTexture3DNode
+ * @x3d 3.3
+ * @component Texturing3D
+ * @status full
+ * @extends x3dom.nodeTypes.X3DTextureNode
+ * @param {Object} [ctx=null] - context object, containing initial settings like namespace
+ * @classdesc This abstract node type is the base type for all node types that specify 3D sources for texture images.
+ */
+ function (ctx) {
+ x3dom.nodeTypes.X3DTexture3DNode.superClass.call(this, ctx);
+
+ }
+ )
+);
+/** @namespace x3dom.nodeTypes */
+/*
+ * X3DOM JavaScript Library
+ * http://www.x3dom.org
+ *
+ * (C)2009 Fraunhofer IGD, Darmstadt, Germany
+ * Dual licensed under the MIT and GPL
+ */
+
+/* ### ComposedTexture3D ### */
+x3dom.registerNodeType(
+ "ComposedTexture3D",
+ "Texturing3D",
+ defineClass(x3dom.nodeTypes.X3DTexture3DNode,
+
+ /**
+ * Constructor for ComposedTexture3D
+ * @constructs x3dom.nodeTypes.ComposedTexture3D
+ * @x3d 3.3
+ * @component Texturing3D
+ * @status full
+ * @extends x3dom.nodeTypes.X3DTexture3DNode
+ * @param {Object} [ctx=null] - context object, containing initial settings like namespace
+ * @classdesc The ComposedTexture3D node defines a 3D image-based texture map as a collection of 2D texture sources at various depths and parameters controlling tiling repetition of the texture onto geometry.
+ */
+ function (ctx) {
+ x3dom.nodeTypes.ComposedTexture3D.superClass.call(this, ctx);
+
+
+ /**
+ * The texture values are interpreted with the first image being at depth 0 and each following image representing an increasing depth value in the R direction.
+ * A user shall provide 2^n source textures in this array. The individual source textures will ignore their repeat field values.
+ * @var {x3dom.fields.MFNode} texture
+ * @memberof x3dom.nodeTypes.ComposedTexture3D
+ * @initvalue x3dom.nodeTypes.X3DTexture3DNode
+ * @field x3d
+ * @instance
+ */
+ this.addField_MFNode('texture', x3dom.nodeTypes.X3DTexture3DNode);
+
+ }
+ )
+);
+/** @namespace x3dom.nodeTypes */
+/*
+ * X3DOM JavaScript Library
+ * http://www.x3dom.org
+ *
+ * (C)2009 Fraunhofer IGD, Darmstadt, Germany
+ * Dual licensed under the MIT and GPL
+ */
+
+/* ### ImageTexture3D ### */
+x3dom.registerNodeType(
+ "ImageTexture3D",
+ "Texturing3D",
+ defineClass(x3dom.nodeTypes.X3DTexture3DNode,
+
+ /**
+ * Constructor for ImageTexture3D
+ * @constructs x3dom.nodeTypes.ImageTexture3D
+ * @x3d 3.3
+ * @component Texturing3D
+ * @status full
+ * @extends x3dom.nodeTypes.X3DTexture3DNode
+ * @param {Object} [ctx=null] - context object, containing initial settings like namespace
+ * @classdesc The ImageTexture3D node defines a texture map by specifying a single image file that contains complete 3D data and general parameters for mapping texels to geometry.
+ * The texture is read from the URL specified by the url field. When the url field contains no values ([]), texturing is disabled.
+ */
+ function (ctx) {
+ x3dom.nodeTypes.ImageTexture3D.superClass.call(this, ctx);
+
+ }
+ )
+);
+/** @namespace x3dom.nodeTypes */
+/*
+ * X3DOM JavaScript Library
+ * http://www.x3dom.org
+ *
+ * (C)2009 Fraunhofer IGD, Darmstadt, Germany
+ * Dual licensed under the MIT and GPL
+ */
+
+/* ### PixelTexture3D ### */
+x3dom.registerNodeType(
+ "PixelTexture3D",
+ "Texturing3D",
+ defineClass(x3dom.nodeTypes.X3DTexture3DNode,
+
+ /**
+ * Constructor for PixelTexture3D
+ * @constructs x3dom.nodeTypes.PixelTexture3D
+ * @x3d 3.3
+ * @component Texturing3D
+ * @status experimental
+ * @extends x3dom.nodeTypes.X3DTexture3DNode
+ * @param {Object} [ctx=null] - context object, containing initial settings like namespace
+ * @classdesc The PixelTexture3D node defines a 3D image-based texture map as an explicit array of pixel values (image field) and parameters controlling tiling repetition of the texture onto geometry.
+ */
+ function (ctx) {
+ x3dom.nodeTypes.PixelTexture3D.superClass.call(this, ctx);
+
+ }
+ )
+);
+/** @namespace x3dom.nodeTypes */
+/*
+ * X3DOM JavaScript Library
+ * http://www.x3dom.org
+ *
+ * (C)2009 Fraunhofer IGD, Darmstadt, Germany
+ * Dual licensed under the MIT and GPL
+ */
+
+/* ### TextureCoordinate3D ### */
+x3dom.registerNodeType(
+ "TextureCoordinate3D",
+ "Texturing3D",
+ defineClass(x3dom.nodeTypes.X3DTextureCoordinateNode,
+
+ /**
+ * Constructor for TextureCoordinate3D
+ * @constructs x3dom.nodeTypes.TextureCoordinate3D
+ * @x3d 3.3
+ * @component Texturing3D
+ * @status full
+ * @extends x3dom.nodeTypes.X3DTextureCoordinateNode
+ * @param {Object} [ctx=null] - context object, containing initial settings like namespace
+ * @classdesc The TextureCoordinate3D node is a geometry property node that specifies a set of 3D texture coordinates used by vertex-based geometry nodes (e.g., IndexedFaceSet and ElevationGrid) to map 3D textures to vertices.
+ */
+ function (ctx) {
+ x3dom.nodeTypes.TextureCoordinate3D.superClass.call(this, ctx);
+
+
+ /**
+ * Specifies the array of texture coordinates.
+ * @var {x3dom.fields.MFVec3f} point
+ * @memberof x3dom.nodeTypes.TextureCoordinate3D
+ * @initvalue []
+ * @field x3d
+ * @instance
+ */
+ this.addField_MFVec3f(ctx, 'point', []);
+
+ }
+ )
+);
+/** @namespace x3dom.nodeTypes */
+/*
+ * X3DOM JavaScript Library
+ * http://www.x3dom.org
+ *
+ * (C)2009 Fraunhofer IGD, Darmstadt, Germany
+ * Dual licensed under the MIT and GPL
+ */
+
+/* ### TextureTransform3D ### */
+x3dom.registerNodeType(
+ "TextureTransform3D",
+ "Texturing3D",
+ defineClass(x3dom.nodeTypes.X3DTextureTransformNode,
+
+ /**
+ * Constructor for TextureTransform3D
+ * @constructs x3dom.nodeTypes.TextureTransform3D
+ * @x3d 3.3
+ * @component Texturing3D
+ * @status full
+ * @extends x3dom.nodeTypes.X3DTextureTransformNode
+ * @param {Object} [ctx=null] - context object, containing initial settings like namespace
+ * The TextureTransform3D node specifies a 3D transformation that is applied to texture coordinates.
+ * This node affects the way texture coordinates are applied to the geometric surface. The transformation consists of (in order):
+ * a translation; a rotation about the centre point; and a non-uniform scale about the centre point.
+ */
+ function (ctx) {
+ x3dom.nodeTypes.TextureTransform3D.superClass.call(this, ctx);
+
+
+ /**
+ * The center field specifies a translation offset in texture coordinate space about which the rotation and scale fields are applied.
+ * @var {x3dom.fields.SFVec3f} center
+ * @memberof x3dom.nodeTypes.TextureTransform3D
+ * @initvalue 0,0,0
+ * @field x3d
+ * @instance
+ */
+ this.addField_SFVec3f(ctx, 'center', 0, 0, 0);
+
+ /**
+ * The rotation field specifies a rotation in angle base units of the texture coordinates about the center point after the scale has been applied.
+ * A positive rotation value makes the texture coordinates rotate counterclockwise about the centre, thereby rotating the appearance of the texture itself clockwise.
+ * @var {x3dom.fields.SFRotation} rotation
+ * @memberof x3dom.nodeTypes.TextureTransform3D
+ * @initvalue 0,0,1,0
+ * @field x3d
+ * @instance
+ */
+ this.addField_SFRotation(ctx, 'rotation', 0, 0, 1, 0);
+
+ /**
+ * The scale field specifies a scaling factor in S and T of the texture coordinates about the center point.
+ * @var {x3dom.fields.SFVec3f} scale
+ * @memberof x3dom.nodeTypes.TextureTransform3D
+ * @initvalue 1,1,1
+ * @field x3d
+ * @instance
+ */
+ this.addField_SFVec3f(ctx, 'scale', 1, 1, 1);
+
+ /**
+ * The translation field specifies a translation of the texture coordinates.
+ * @var {x3dom.fields.SFVec3f} translation
+ * @memberof x3dom.nodeTypes.TextureTransform3D
+ * @initvalue 0,0,0
+ * @field x3d
+ * @instance
+ */
+ this.addField_SFVec3f(ctx, 'translation', 0, 0, 0);
+
+ /**
+ *
+ * @var {x3dom.fields.SFRotation} scaleOrientation
+ * @memberof x3dom.nodeTypes.TextureTransform3D
+ * @initvalue 0,0,1,0
+ * @field x3d
+ * @instance
+ */
+ this.addField_SFRotation(ctx, 'scaleOrientation', 0, 0, 1, 0);
+
+ }
+ )
+);
+/** @namespace x3dom.nodeTypes */
+/*
+ * X3DOM JavaScript Library
+ * http://www.x3dom.org
+ *
+ * (C)2009 Fraunhofer IGD, Darmstadt, Germany
+ * Dual licensed under the MIT and GPL
+ */
+
+/* ### TextureTransformMatrix3D ### */
+x3dom.registerNodeType(
+ "TextureTransformMatrix3D",
+ "Texturing3D",
+ defineClass(x3dom.nodeTypes.X3DTextureTransformNode,
+
+ /**
+ * Constructor for TextureTransformMatrix3D
+ * @constructs x3dom.nodeTypes.TextureTransformMatrix3D
+ * @x3d 3.3
+ * @component Texturing3D
+ * @status full
+ * @extends x3dom.nodeTypes.X3DTextureTransformNode
+ * @param {Object} [ctx=null] - context object, containing initial settings like namespace
+ * @classdesc The TextureTransform3D node specifies a 3D transformation that is applied to texture coordinates.
+ * This node affects the way texture coordinates are applied to the geometric surface.
+ * The transformation consists of a transformation matrix.
+ */
+ function (ctx) {
+ x3dom.nodeTypes.TextureTransformMatrix3D.superClass.call(this, ctx);
+
+
+ /**
+ * The matrix field specifies a generalized, unfiltered 4×4 transformation matrix that can be used to modify the texture. Any set of values is permitted.
+ * @var {x3dom.fields.SFMatrix4f} matrix
+ * @memberof x3dom.nodeTypes.TextureTransformMatrix3D
+ * @initvalue 1,0,0,0
+ * @field x3dom
+ * @instance
+ */
+ this.addField_SFMatrix4f(ctx, 'matrix',
+ 1, 0, 0, 0,
+ 0, 1, 0, 0,
+ 0, 0, 1, 0,
+ 0, 0, 0, 1);
+
+ }
+ )
+);
+/** @namespace x3dom.nodeTypes */
+/*
+ * X3DOM JavaScript Library
+ * http://www.x3dom.org
+ *
+ * (C)2009 Fraunhofer IGD, Darmstadt, Germany
+ * Dual licensed under the MIT and GPL
+ */
+
+/**
+ * The abstract pointing device sensor node class serves as a base class for all pointing device sensors.
+ * Pointing device sensors catch pointing device events from all sibling nodes.
+ */
+
+x3dom.registerNodeType(
+ "X3DPointingDeviceSensorNode",
+ "PointingDeviceSensor",
+ defineClass(x3dom.nodeTypes.X3DSensorNode,
+
+ /**
+ * Constructor for X3DPointingDeviceSensorNode
+ * @constructs x3dom.nodeTypes.X3DPointingDeviceSensorNode
+ * @x3d 3.3
+ * @component PointingDeviceSensor
+ * @status experimental
+ * @extends x3dom.nodeTypes.X3DSensorNode
+ * @param {Object} [ctx=null] - context object, containing initial settings like namespace
+ * @classdesc An abstract base class for all pointing device sensor nodes.
+ */
+ function (ctx)
+ {
+ x3dom.nodeTypes.X3DPointingDeviceSensorNode.superClass.call(this, ctx);
+
+ //---------------------------------------
+ // FIELDS
+ //---------------------------------------
+
+ //route-able output fields
+ //this.addField_SFBool(ctx, 'isOver', false);
+
+
+ //---------------------------------------
+ // PROPERTIES
+ //---------------------------------------
+ },
+ {
+ //----------------------------------------------------------------------------------------------------------------------
+ // PUBLIC FUNCTIONS
+ //----------------------------------------------------------------------------------------------------------------------
+
+ /**
+ * Function that gets called if the pointing device has been pressed over a sibling node of this sensor
+ * @param {DOMEvent} event - the pointer event
+ * @private
+ */
+ pointerPressedOverSibling: function(event)
+ {
+ if (this._vf.enabled)
+ {
+ this._vf.isActive = true;
+ this.postMessage('isActive', true);
+ }
+ },
+
+ //----------------------------------------------------------------------------------------------------------------------
+
+ /**
+ * Function that gets called if the pointing device has been moved,
+ * after it has been pressed over a sibling of this node
+ * @param {DOMEvent} event - the pointer event
+ * @private
+ */
+ pointerMoved: function(event)
+ {
+
+ },
+
+ //----------------------------------------------------------------------------------------------------------------------
+
+ /**
+ * Function that gets called if the pointing device has entered a sibling of this node.
+ * @param {DOMEvent} event - the pointer event
+ */
+ pointerMovedOver: function(event)
+ {
+ if (this._vf.enabled)
+ {
+ this.postMessage('isOver', true);
+ }
+ },
+
+ //----------------------------------------------------------------------------------------------------------------------
+
+ /**
+ * Function that gets called if the pointing device has left a sibling of this node.
+ * @param {DOMEvent} event - the pointer event
+ */
+ pointerMovedOut: function(event)
+ {
+ if (this._vf.enabled)
+ {
+ this.postMessage('isOver', false);
+ }
+ },
+
+ //----------------------------------------------------------------------------------------------------------------------
+
+ /**
+ * Function that gets called if the pointing device has been released,
+ * after it has been pressed over a sibling of this node
+ * @private
+ */
+ pointerReleased: function()
+ {
+ if (this._vf.enabled)
+ {
+ this._vf.isActive = false;
+ this.postMessage('isActive', false);
+ }
+ }
+
+ //----------------------------------------------------------------------------------------------------------------------
+ }
+ )
+);
+
+/** @namespace x3dom.nodeTypes */
+/*
+ * X3DOM JavaScript Library
+ * http://www.x3dom.org
+ *
+ * (C)2009 Fraunhofer IGD, Darmstadt, Germany
+ * Dual licensed under the MIT and GPL
+ */
+
+/**
+ * The abstract drag sensor node class serves as a base class for all drag-style pointing device sensors.
+ */
+
+x3dom.registerNodeType(
+ "X3DDragSensorNode",
+ "PointingDeviceSensor",
+ defineClass(x3dom.nodeTypes.X3DPointingDeviceSensorNode,
+
+ /**
+ * Constructor for X3DDragSensorNode
+ * @constructs x3dom.nodeTypes.X3DDragSensorNode
+ * @x3d 3.3
+ * @component PointingDeviceSensor
+ * @status experimental
+ * @extends x3dom.nodeTypes.X3DPointingDeviceSensorNode
+ * @param {Object} [ctx=null] - context object, containing initial settings like namespace
+ * @classdesc An abstract base class for all sensors that are processing drag gestures of the pointer.
+ */
+ function (ctx)
+ {
+ x3dom.nodeTypes.X3DDragSensorNode.superClass.call(this, ctx);
+
+ //---------------------------------------
+ // FIELDS
+ //---------------------------------------
+
+ /**
+ * Determines whether offset values from previous drag gestures are remembered / accumulated.
+ * @var {x3dom.fields.SFBool} autoOffset
+ * @memberof x3dom.nodeTypes.X3DDragSensorNode
+ * @initvalue true
+ * @field x3d
+ * @instance
+ */
+ this.addField_SFBool(ctx, 'autoOffset', true);
+
+ //route-able output fields
+ //this.addField_SFVec3f(ctx, 'trackPoint_changed', 0, 0, 0);
+
+
+ //---------------------------------------
+ // PROPERTIES
+ //---------------------------------------
+
+ //TODO: revise if those are still needed
+ /**
+ * Last mouse position in x direction.
+ * @var {Double} _lastX
+ * @private
+ */
+ this._lastX = -1;
+
+ /**
+ * Last mouse position in y direction.
+ * @var {Double} _lastY
+ * @private
+ */
+ this._lastY = -1;
+ },
+ {
+ //----------------------------------------------------------------------------------------------------------
+ // PUBLIC FUNCTIONS
+ //----------------------------------------------------------------------------------------------------------
+
+ /**
+ * @overrides x3dom.nodeTypes.X3DPointingDeviceSensorNode._pointerPressedOverSibling
+ * @param {DOMEvent} event - the pointer event
+ * @private
+ */
+ pointerPressedOverSibling: function(event)
+ {
+ x3dom.nodeTypes.X3DPointingDeviceSensorNode.prototype.pointerPressedOverSibling.call(this, event);
+
+ this._lastX = event.layerX;
+ this._lastY = event.layerY;
+
+ this._startDragging(event.viewarea, event.layerX, event.layerX, event.worldX, event.worldY, event.worldZ);
+ },
+
+ //----------------------------------------------------------------------------------------------------------
+
+ /**
+ * @overrides x3dom.nodeTypes.X3DPointingDeviceSensorNode._pointerMoved
+ * @param {DOMEvent] event - the pointer event
+ * @private
+ */
+ pointerMoved: function(event)
+ {
+ x3dom.nodeTypes.X3DPointingDeviceSensorNode.prototype.pointerMoved.call(this, event);
+
+ if (this._vf.isActive && this._vf.enabled)
+ {
+ this._process2DDrag(event.layerX,
+ event.layerY,
+ event.layerX-this._lastX,
+ event.layerY-this._lastY);
+ }
+ },
+
+ //----------------------------------------------------------------------------------------------------------
+
+ /**
+ * @overrides x3dom.nodeTypes.X3DPointingDeviceSensorNode._pointerReleased
+ * @private
+ */
+ pointerReleased: function()
+ {
+ x3dom.nodeTypes.X3DPointingDeviceSensorNode.prototype.pointerReleased.call(this);
+
+ this._stopDragging();
+ },
+
+ //----------------------------------------------------------------------------------------------------------
+
+ //----------------------------------------------------------------------------------------------------------
+ // PRIVATE FUNCTIONS
+ //----------------------------------------------------------------------------------------------------------
+
+ /**
+ * Function that is called as soon as a drag action is initiated.
+ * @param {x3dom.Viewarea} viewarea - the viewarea which initiated the drag operation
+ * @param {Double} x - 2D pointer x coordinate at the time of the dragging initiation
+ * @param {Double} y - 2D pointer y coordinate at the time of the dragging initiation
+ * @param {Double} wx - 3D world x pick coordinate on the sensor geometry at the time of the dragging initiation
+ * @param {Double} wy - 3D world x pick coordinate on the sensor geometry at the time of the dragging initiation
+ * @param {Double} wz - 3D world z pick coordinate on the sensor geometry at the time of the dragging initiation
+ * @private
+ */
+ _startDragging: function(viewarea, x, y, wx, wy, wz)
+ {
+
+ },
+
+ //----------------------------------------------------------------------------------------------------------
+
+ /**
+ * Processes a 2D drag action, using the given 2D delta values.
+ * @param {Double} x - 2D pointer x coordinate at the time of the dragging initiation
+ * @param {Double} y - 2D pointer y coordinate at the time of the dragging initiation
+ * @param {Double} dx - delta of x, with respect to the last time the function was invoked
+ * @param {Double} dy - delta of Y, with respect to the last time the function was invoked
+ * @private
+ */
+ _process2DDrag: function(x, y, dx, dy)
+ {
+
+ },
+
+ //----------------------------------------------------------------------------------------------------------
+
+ /**
+ * Function that is called as soon as a drag action is initiated.
+ * @private
+ */
+ _stopDragging: function()
+ {
+
+ }
+
+ //----------------------------------------------------------------------------------------------------------
+ }
+ )
+);
+
+/** @namespace x3dom.nodeTypes */
+/*
+ * X3DOM JavaScript Library
+ * http://www.x3dom.org
+ *
+ * (C)2009 Fraunhofer IGD, Darmstadt, Germany
+ * Dual licensed under the MIT and GPL
+ */
+
+x3dom.registerNodeType(
+ "X3DTouchSensorNode",
+ "PointingDeviceSensor",
+ defineClass(x3dom.nodeTypes.X3DPointingDeviceSensorNode,
+
+ /**
+ * Constructor for X3DTouchSensorNode
+ * @constructs x3dom.nodeTypes.X3DTouchSensorNode
+ * @x3d 3.3
+ * @component PointingDeviceSensor
+ * @status experimental
+ * @extends x3dom.nodeTypes.X3DPointingDeviceSensorNode
+ * @param {Object} [ctx=null] - context object, containing initial settings like namespace
+ * @classdesc An abstract base class for all sensors that process touch events.
+ */
+ function (ctx)
+ {
+ x3dom.nodeTypes.X3DTouchSensorNode.superClass.call(this, ctx);
+
+ //---------------------------------------
+ // FIELDS
+ //---------------------------------------
+
+ //route-able output fields
+ //this.addField_SFTime(ctx, 'touchTime', 0);
+
+
+ //---------------------------------------
+ // PROPERTIES
+ //---------------------------------------
+ },
+ {
+ //----------------------------------------------------------------------------------------------------------------------
+ // PUBLIC FUNCTIONS
+ //----------------------------------------------------------------------------------------------------------------------
+
+
+ }
+ )
+);
+
+/** @namespace x3dom.nodeTypes */
+/*
+ * X3DOM JavaScript Library
+ * http://www.x3dom.org
+ *
+ * (C)2009 Fraunhofer IGD, Darmstadt, Germany
+ * Dual licensed under the MIT and GPL
+ */
+
+x3dom.registerNodeType(
+ "TouchSensor",
+ "PointingDeviceSensor",
+ defineClass(x3dom.nodeTypes.X3DTouchSensorNode,
+
+ /**
+ * Constructor for TouchSensor
+ * @constructs x3dom.nodeTypes.TouchSensor
+ * @x3d 3.3
+ * @component PointingDeviceSensor
+ * @status experimental
+ * @extends x3dom.nodeTypes.X3DDragSensorNode
+ * @param {Object} [ctx=null] - context object, containing initial settings like namespace
+ * @classdesc TouchSensor tracks location and state of the pointing device, and detects when user points at
+ * geometry. Hint: X3DOM, running in an HTML environment, you actually don't need this node, as you can
+ * simply use HTML events (like onclick) on your nodes. However, this node is implemented to complete the
+ * pointing device sensor component, and it may be useful to ensure compatibility with older X3D scene content.
+ */
+ function (ctx)
+ {
+ x3dom.nodeTypes.TouchSensor.superClass.call(this, ctx);
+
+ //---------------------------------------
+ // FIELDS
+ //---------------------------------------
+
+ //route-able output fields
+ //this.addField_SFVec3f(ctx, 'hitNormal_changed', 0 0 0);
+ //this.addField_SFVec3f(ctx, 'hitPoint_changed', 0 0 0);
+ //this.addField_SFVec2f(ctx, 'hitTexCoord_changed', 0 0);
+
+
+ //---------------------------------------
+ // PROPERTIES
+ //---------------------------------------
+ },
+ {
+ //----------------------------------------------------------------------------------------------------------------------
+ // PUBLIC FUNCTIONS
+ //----------------------------------------------------------------------------------------------------------------------
+
+
+ }
+ )
+);
+
+/** @namespace x3dom.nodeTypes */
+/*
+ * X3DOM JavaScript Library
+ * http://www.x3dom.org
+ *
+ * (C)2009 Fraunhofer IGD, Darmstadt, Germany
+ * Dual licensed under the MIT and GPL
+ */
+
+/**
+ * The plane sensor node translates drag gestures, performed with a pointing device like a mouse,
+ * into 3D transformations.
+ */
+
+
+x3dom.registerNodeType(
+ "PlaneSensor",
+ "PointingDeviceSensor",
+ defineClass(x3dom.nodeTypes.X3DDragSensorNode,
+
+ /**
+ * Constructor for PlaneSensor
+ * @constructs x3dom.nodeTypes.PlaneSensor
+ * @x3d 3.3
+ * @component PointingDeviceSensor
+ * @status experimental
+ * @extends x3dom.nodeTypes.X3DDragSensorNode
+ * @param {Object} [ctx=null] - context object, containing initial settings like namespace
+ * @classdesc PlaneSensor converts pointing device motion into 2D translation, parallel to the local Z=0 plane.
+ * Hint: You can constrain translation output to one axis by setting the respective minPosition and maxPosition
+ * members to equal values for that axis.
+ */
+ function (ctx)
+ {
+ x3dom.nodeTypes.PlaneSensor.superClass.call(this, ctx);
+
+ //---------------------------------------
+ // FIELDS
+ //---------------------------------------
+ /**
+ * The local sensor coordinate system is created by additionally applying the axisRotation field value to
+ * the local coordinate system of the sensor node.
+ * @var {x3dom.fields.SFRotation} axisRotation
+ * @memberof x3dom.nodeTypes.PlaneSensor
+ * @initvalue 0,0,1,0
+ * @field x3d
+ * @instance
+ */
+ this.addField_SFRotation(ctx, 'axisRotation', 0, 0, 1, 0);
+
+
+ /**
+ * The minPosition and maxPosition fields allow to constrain the 2D output of the plane sensor, along each
+ * 2D component. If the value of a component in maxPosition is smaller than the value of a component in
+ * minPosition, output is not constrained along the corresponding direction.
+ * @var {x3dom.fields.SFVec2f} minPosition
+ * @memberof x3dom.nodeTypes.PlaneSensor
+ * @initvalue 0,0
+ * @field x3d
+ * @instance
+ */
+ this.addField_SFVec2f(ctx, 'minPosition', 0, 0);
+
+
+ /**
+ * The minPosition and maxPosition fields allow to constrain the 2D output of the plane sensor, along each
+ * 2D component. If the value of a component in maxPosition is smaller than the value of a component in
+ * minPosition, output is not constrained along the corresponding direction.
+ * @var {x3dom.fields.SFVec2f} maxPosition
+ * @memberof x3dom.nodeTypes.PlaneSensor
+ * @initvalue -1,-1
+ * @field x3d
+ * @instance
+ */
+ this.addField_SFVec2f(ctx, 'maxPosition', -1, -1);
+
+
+ /**
+ * Offset value that is incorporated into the translation output of the sensor.
+ * This value is automatically updated if the value of the autoOffset field is 'true'.
+ * @var {x3dom.fields.SFVec3f} offset
+ * @memberof x3dom.nodeTypes.PlaneSensor
+ * @initvalue 0,0,0
+ * @field x3d
+ * @instance
+ */
+ this.addField_SFVec3f(ctx, 'offset', 0, 0, 0);
+
+ //route-able output fields
+ //this.addField_SFVec3f(ctx, 'translation_changed', 0, 0, 0);
+
+
+ //---------------------------------------
+ // PROPERTIES
+ //---------------------------------------
+
+ /**
+ *
+ * @type {x3dom.fields.Quaternion}
+ * @private
+ */
+ //TODO: update on change
+ this._rotationMatrix = this._vf.axisRotation.toMatrix();
+
+ /**
+ * World-To-Local matrix for this node, including the axisRotation of the sensor
+ */
+ this._worldToLocalMatrix = null;
+
+
+ /**
+ * Initial intersection point with the sensor's plane, at the time the sensor was activated
+ * @type {x3dom.fields.SFVec3f}
+ * @private
+ */
+ this._initialPlaneIntersection = null;
+
+ /**
+ * Plane normal, computed on drag start and used during dragging to compute plane intersections
+ * @type {x3dom.fields.SFVec3f}
+ * @private
+ */
+ this._planeNormal = null;
+
+ /**
+ * Current viewarea that is used for dragging, needed for ray setup to compute the plane intersection
+ *
+ * @type {x3dom.Viewarea}
+ * @private
+ */
+ this._viewArea = null;
+
+ /**
+ * Current translation that is produced by this drag sensor
+ * @type {x3dom.fields.SFVec3f}
+ * @private
+ */
+ this._currentTranslation = new x3dom.fields.SFVec3f(0.0, 0.0, 0.0);
+ },
+ {
+ //----------------------------------------------------------------------------------------------------------------------
+ // PUBLIC FUNCTIONS
+ //----------------------------------------------------------------------------------------------------------------------
+
+ /**
+ * This function returns the parent transformation of this node, combined with its current axisRotation
+ * @overrides x3dom.nodeTypes.X3DPointingDeviceSensorNode.getCurrentTransform
+ */
+ getCurrentTransform: function ()
+ {
+ var parentTransform = x3dom.nodeTypes.X3DDragSensorNode.prototype.getCurrentTransform.call(this);
+
+ return parentTransform.mult(this._rotationMatrix);
+ },
+
+ //----------------------------------------------------------------------------------------------------------------------
+ // PRIVATE FUNCTIONS
+ //----------------------------------------------------------------------------------------------------------------------
+
+ /**
+ * @overrides x3dom.nodeTypes.X3DDragSensorNode.prototype._startDragging
+ * @private
+ */
+ _startDragging: function(viewarea, x, y, wx, wy, wz)
+ {
+ x3dom.nodeTypes.X3DDragSensorNode.prototype._startDragging.call(this, viewarea, x, y, wx, wy, wz);
+
+ this._viewArea = viewarea;
+
+ this._currentTranslation = new x3dom.fields.SFVec3f(0.0, 0.0, 0.0).add(this._vf.offset);
+
+ //TODO: handle multi-path nodes
+
+ //get model matrix for this node, combined with the axis rotation
+ this._worldToLocalMatrix = this.getCurrentTransform().inverse();
+
+ //remember initial point of intersection with the plane, transform it to local sensor coordinates
+ this._initialPlaneIntersection = this._worldToLocalMatrix.multMatrixPnt(new x3dom.fields.SFVec3f(wx, wy, wz));
+
+ //compute plane normal in local coordinates
+ this._planeNormal = new x3dom.fields.SFVec3f(0.0, 0.0, 1.0);
+ },
+
+ //----------------------------------------------------------------------------------------------------------------------
+
+ /**
+ * @overrides x3dom.nodeTypes.X3DDragSensorNode._process2DDrag
+ * @private
+ */
+ _process2DDrag: function(x, y, dx, dy)
+ {
+ x3dom.nodeTypes.X3DDragSensorNode.prototype._process2DDrag.call(this, x, y, dx, dy);
+
+ var intersectionPoint;
+ var minPos, maxPos;
+
+ if (this._initialPlaneIntersection)
+ {
+ //compute point of intersection with the plane
+ var viewRay = this._viewArea.calcViewRay(x, y);
+
+ //transform the world coordinates, used for the ray, to local sensor coordinates
+ viewRay.pos = this._worldToLocalMatrix.multMatrixPnt(viewRay.pos);
+ viewRay.dir = this._worldToLocalMatrix.multMatrixVec(viewRay.dir.normalize());
+
+ intersectionPoint = viewRay.intersectPlane(this._initialPlaneIntersection, this._planeNormal);
+
+ //allow interaction from both sides of the plane
+ if (!intersectionPoint)
+ {
+ intersectionPoint = viewRay.intersectPlane(this._initialPlaneIntersection, this._planeNormal.negate());
+ }
+
+ if (intersectionPoint)
+ {
+ //compute difference between new point of intersection and initial point
+ this._currentTranslation = intersectionPoint.subtract(this._initialPlaneIntersection);
+ this._currentTranslation = this._currentTranslation.add(this._vf.offset);
+
+ //clamp translation components, if desired
+ minPos = this._vf.minPosition;
+ maxPos = this._vf.maxPosition;
+
+ if (minPos.x <= maxPos.x)
+ {
+ this._currentTranslation.x = Math.min(this._currentTranslation.x, maxPos.x);
+ this._currentTranslation.x = Math.max(this._currentTranslation.x, minPos.x);
+ }
+ if (minPos.y <= maxPos.y)
+ {
+ this._currentTranslation.y = Math.min(this._currentTranslation.y, maxPos.y);
+ this._currentTranslation.y = Math.max(this._currentTranslation.y, minPos.y);
+ }
+
+ //output translation_changed event
+ this.postMessage('translation_changed', x3dom.fields.SFVec3f.copy(this._currentTranslation));
+ }
+ }
+ },
+
+ //----------------------------------------------------------------------------------------------------------------------
+
+ /**
+ * @overrides x3dom.nodeTypes.X3DDragSensorNode._stopDragging
+ * @private
+ */
+ _stopDragging: function()
+ {
+ x3dom.nodeTypes.X3DDragSensorNode.prototype._stopDragging.call(this);
+
+ if (this._vf.autoOffset)
+ {
+ this._vf.offset = x3dom.fields.SFVec3f.copy(this._currentTranslation);
+ this.postMessage('offset_changed', this._vf.offset);
+ }
+ }
+
+ //----------------------------------------------------------------------------------------------------------------------
+ }
+ )
+);
+/** @namespace x3dom.nodeTypes */
+/*
+ * X3DOM JavaScript Library
+ * http://www.x3dom.org
+ *
+ * (C)2009 Fraunhofer IGD, Darmstadt, Germany
+ * Dual licensed under the MIT and GPL
+ */
+
+ /*
+ * Based on code provided by Fraunhofer IGD.
+ * (C)2014 Toshiba Corporation, Japan.
+ * Dual licensed under the MIT and GPL.
+ */
+
+x3dom.registerNodeType(
+ "SphereSensor",
+ "PointingDeviceSensor",
+ defineClass(x3dom.nodeTypes.X3DDragSensorNode,
+
+ /**
+ * Constructor for SphereSensor
+ * @constructs x3dom.nodeTypes.SphereSensor
+ * @x3d 3.3
+ * @component PointingDeviceSensor
+ * @status experimental
+ * @extends x3dom.nodeTypes.X3DDragSensorNode
+ * @param {Object} [ctx=null] - context object, containing initial settings like namespace
+ * @classdesc SphereSensor converts pointing device motion into a spherical rotation around the origin of the
+ * local coordinate system.
+ */
+ function (ctx)
+ {
+ x3dom.nodeTypes.SphereSensor.superClass.call(this, ctx);
+
+ //---------------------------------------
+ // FIELDS
+ //---------------------------------------
+ /**
+ * Offset value that is incorporated into the rotation output of the sensor.
+ * This value is automatically updated if the value of the autoOffset field is 'true'.
+ * @var {x3dom.fields.SFRotation} offset
+ * @memberof x3dom.nodeTypes.SphereSensor
+ * @initvalue 0,1,0,0
+ * @field x3d
+ * @instance
+ */
+ this.addField_SFRotation(ctx, 'offset', 0, 1, 0, 0);
+
+ //route-able output fields
+ //this.addField_SFVec3f(ctx, 'rotation_changed', 0, 0, 0);
+
+
+ //---------------------------------------
+ // PROPERTIES
+ //---------------------------------------
+
+ /**
+ * Current rotation that is produced by this sphere sensor
+ * @type {x3dom.fields.Quaternion}
+ * @private
+ */
+ this._currentRotation = null;
+
+ /**
+ * Rotation matrix, derived from the current value of the offset field
+ * @type {x3dom.fields.SFMatrix4f}
+ * @private
+ */
+ this._rotationMatrix = this._vf.offset.toMatrix();
+ },
+ {
+ //----------------------------------------------------------------------------------------------------------------------
+ // PUBLIC FUNCTIONS
+ //----------------------------------------------------------------------------------------------------------------------
+
+ /**
+ * This function returns the parent transformation of this node, combined with its current rotation
+ * @overrides x3dom.nodeTypes.X3DPointingDeviceSensorNode.getCurrentTransform
+ */
+ getCurrentTransform: function ()
+ {
+ var parentTransform = x3dom.nodeTypes.X3DDragSensorNode.prototype.getCurrentTransform.call(this);
+
+ return parentTransform.mult(this._rotationMatrix);
+ },
+
+
+ //----------------------------------------------------------------------------------------------------------------------
+ // PRIVATE FUNCTIONS
+ //----------------------------------------------------------------------------------------------------------------------
+
+ /**
+ * @overrides x3dom.nodeTypes.X3DDragSensorNode.prototype._startDragging
+ * @private
+ */
+ _startDragging: function(viewarea, x, y, wx, wy, wz)
+ {
+ //console.log(viewarea, x, y, wx, wy, wz);
+ x3dom.nodeTypes.X3DDragSensorNode.prototype._startDragging.call(this, viewarea, x, y, wx, wy, wz);
+
+ this._currentRotation = new x3dom.fields.Quaternion();
+
+ this._viewArea = viewarea;
+
+ // origin of sphere in local coordinates
+ this._localOrigin = new x3dom.fields.SFVec3f(0.0, 0.0, 0.0);
+
+ this._inverseToWorldMatrix = this.getCurrentTransform().inverse();
+
+ // compute initial point of intersection on the sphere sensor's geometry, in local sphere sensor's coordinate system
+ var firstIntersection = this._inverseToWorldMatrix.multMatrixPnt(new x3dom.fields.SFVec3f(wx, wy, wz));
+
+ this._initialSphereIntersectionVector = firstIntersection.subtract(this._localOrigin);
+
+ this._sphereRadius = this._initialSphereIntersectionVector.length();
+
+ this._initialSphereIntersectionVector = this._initialSphereIntersectionVector.normalize();
+ },
+
+ //----------------------------------------------------------------------------------------------------------------------
+
+ /**
+ * @overrides x3dom.nodeTypes.X3DDragSensorNode._process2DDrag
+ * @private
+ */
+ _process2DDrag: function(x, y, dx, dy)
+ {
+ x3dom.nodeTypes.X3DDragSensorNode.prototype._process2DDrag.call(this, x, y, dx, dy);
+
+ // We have to compute hit point on virtual sphere's geometry
+ var viewRay = this._viewArea.calcViewRay(x, y);
+ viewRay.pos = this._inverseToWorldMatrix.multMatrixPnt(viewRay.pos);
+ viewRay.dir = this._inverseToWorldMatrix.multMatrixVec(viewRay.dir);
+
+ /*
+ * S := Ray Origin = viewRay.pos
+ * V := Ray Direction = viewRay.dir
+ * O := Sphere Center = this._localOrigin
+ * r := Sphere Radius = this._sphereRadius
+ * alpha := Ray parameter
+ *
+ * If the view ray intersects the virtual sphere centred at O
+ * at (S + alpha*V), it must satisfy the following equation:
+ * | S + alpha*V - O | = r
+ * dot_prod((S + alpha*V - O),(S + alpha*V - O)) = r*r
+ * or,
+ * alpha*alpha*V.V + alpha*2*(V.(S-O)) + (S.S -2O.S + O.O) - r*r = 0
+ * or,
+ * A*alpha*alpha + B*alpha + C = 0
+ */
+
+ var A = viewRay.dir.dot(viewRay.dir);
+ var B = 2.0*(viewRay.dir.dot(viewRay.pos.subtract(this._localOrigin)));
+ var C = viewRay.pos.dot(viewRay.pos) - 2.0*this._localOrigin.dot(viewRay.pos) +
+ this._localOrigin.dot(this._localOrigin) - this._sphereRadius*this._sphereRadius;
+
+ var determinant = (B*B) - (4.0*A*C);
+ var alpha_1;
+ var alpha_2;
+
+ // if the roots are real i.e. the ray intersects the sphere, the determinant must be greater
+ // than or equal to zero
+ if(determinant >= 0.0) {
+ alpha_1 = (-B + Math.sqrt(determinant)) / (2.0*A);
+ alpha_2 = (-B - Math.sqrt(determinant)) / (2.0*A);
+
+ // pick the closer of the two points
+ alpha_1 = Math.min(alpha_1, alpha_2);
+
+ // if the closer intersection point has alpha < 0, then we are inside the sphere and must not do anything
+ if(alpha_1 >= 1.0) {
+ //TODO: output trackPoint_changed event
+ var hitPoint = viewRay.pos.add(viewRay.dir.multiply(alpha_1));
+
+ var vecToHitPoint = hitPoint.subtract(this._localOrigin).normalize();
+
+ this._currentRotation = x3dom.fields.Quaternion.rotateFromTo(this._initialSphereIntersectionVector, vecToHitPoint);
+
+ this._currentRotation = this._currentRotation.multiply(this._vf.offset);
+
+ // output rotationChanged_event, given in local sphere sensor coordinates
+ this.postMessage('rotation_changed', this._currentRotation);
+ }
+ }
+ else {
+ // do nothing, because no intersection
+ }
+ },
+
+ //----------------------------------------------------------------------------------------------------------------------
+
+ /**
+ * @overrides x3dom.nodeTypes.X3DDragSensorNode._stopDragging
+ * @private
+ */
+ _stopDragging: function()
+ {
+ x3dom.nodeTypes.X3DDragSensorNode.prototype._stopDragging.call(this);
+
+ if (this._vf.autoOffset)
+ {
+ this._vf.offset = this._currentRotation;
+ this.postMessage('offset_changed', this._vf.offset);
+ }
+
+ this._currentRotation = new x3dom.fields.Quaternion();
+ }
+
+ //----------------------------------------------------------------------------------------------------------------------
+ }
+ )
+);
+/** @namespace x3dom.nodeTypes */
+/*
+ * X3DOM JavaScript Library
+ * http://www.x3dom.org
+ *
+ * (C)2009 Fraunhofer IGD, Darmstadt, Germany
+ * Dual licensed under the MIT and GPL
+ */
+
+x3dom.registerNodeType(
+ "CylinderSensor",
+ "PointingDeviceSensor",
+ defineClass(x3dom.nodeTypes.X3DDragSensorNode,
+
+ /**
+ * Constructor for CylinderSensor
+ * @constructs x3dom.nodeTypes.CylinderSensor
+ * @x3d 3.3
+ * @component PointingDeviceSensor
+ * @status experimental
+ * @extends x3dom.nodeTypes.X3DDragSensorNode
+ * @param {Object} [ctx=null] - context object, containing initial settings like namespace
+ * @classdesc The CylinderSensor node converts pointer motion (for example, from a mouse) into rotation values,
+ * using an invisible cylinder of infinite height, aligned with local Y-axis.
+ */
+ function (ctx)
+ {
+ x3dom.nodeTypes.CylinderSensor.superClass.call(this, ctx);
+
+ //---------------------------------------
+ // FIELDS
+ //---------------------------------------
+ /**
+ * Offset value, in radians, that is incorporated into the rotation output of the sensor.
+ * This value is automatically updated if the value of the autoOffset field is 'true'.
+ * @var {x3dom.fields.SFFloat} offset
+ * @memberof x3dom.nodeTypes.CylinderSensor
+ * @initvalue 0
+ * @field x3d
+ * @instance
+ */
+ this.addField_SFFloat(ctx, 'offset', 0);
+
+
+ /**
+ * The local sensor coordinate system is created by additionally applying the axisRotation field value to
+ * the local coordinate system of the sensor node.
+ * @var {x3dom.fields.SFRotation} axisRotation
+ * @memberof x3dom.nodeTypes.CylinderSensor
+ * @initvalue 0,1,0,0
+ * @field x3d
+ * @instance
+ */
+ this.addField_SFRotation(ctx, 'axisRotation', 0, 1, 0, 0);
+
+
+ /**
+ * Specifies whether the virtual cylinder's lateral surface or end-cap disks of virtual-geometry sensor are
+ * used for manipulation: If the vertical acute angle between the vector from the viewer to the point of
+ * intersection with the sensor geometry and the local Y axis of the cylinder is greater than or equal to
+ * the value of this field, the sensor uses a virtual cylinder to compute rotation output.
+ * Otherwise, if the angle is smaller than the value of this field, the sensor uses a virtual disk instead.
+ * This value of this field is specified in radians.
+ *
+ * ATTENTION: The value of this field is currently ignored.
+ * The cylinder sensor will always operate in cylinder mode.
+ *
+ * @var {x3dom.fields.SFFloat} axisRotation
+ * @memberof x3dom.nodeTypes.CylinderSensor
+ * @initvalue pi/2
+ * @field x3d
+ * @instance
+ */
+ this.addField_SFFloat(ctx, 'diskAngle', 0.262); //this is the official default value, PI/12
+
+
+ /**
+ * The minAngle and maxAngle fields, given in radians, allow to constrain the rotation output of the
+ * cylinder sensor.
+ * If the value of maxAngle is smaller than the value of minAngle, output is not constrained.
+ * @var {x3dom.fields.SFFloat} axisRotation
+ * @memberof x3dom.nodeTypes.CylinderSensor
+ * @initvalue 0
+ * @field x3d
+ * @instance
+ */
+ this.addField_SFFloat(ctx, 'minAngle', 0);
+
+
+ /**
+ * The minAngle and maxAngle fields, given in radians, allow to constrain the rotation output of the
+ * cylinder sensor.
+ * If the value of maxAngle is smaller than the value of minAngle, output is not constrained.
+ * @var {x3dom.fields.SFFloat} axisRotation
+ * @memberof x3dom.nodeTypes.CylinderSensor
+ * @initvalue -1
+ * @field x3d
+ * @instance
+ */
+ this.addField_SFFloat(ctx, 'maxAngle', -1);
+
+ //route-able output fields
+ //this.addField_SFRotation(ctx, 'rotation_changed', 0, 0, 1, 0);
+
+
+ //---------------------------------------
+ // PROPERTIES
+ //---------------------------------------
+
+ /**
+ * Rotation matrix, derived from the current value of the axisRotation field
+ * @type {x3dom.fields.SFMatrix4f}
+ * @private
+ */
+ //TODO: updates
+ this._rotationMatrix = this._vf.axisRotation.toMatrix();
+
+ /**
+ * Current value of the matrix that transforms world coordinates to local sensor coordinates
+ * @type {x3dom.fields.SFMatrix4f}
+ * @private
+ */
+ this._inverseToWorldMatrix = null;
+
+ /**
+ * Vector from the virtual local y-Axis to the initial intersection point with the virtual cylinder,
+ * at the time the sensor was activated
+ * @type {x3dom.fields.SFVec3f}
+ * @private
+ */
+ this._initialCylinderIntersectionVector = null;
+
+ /**
+ * Current viewarea that is used for dragging, needed for ray setup to compute the cylinder intersection
+ *
+ * @type {x3dom.Viewarea}
+ * @private
+ */
+ this._viewArea = null;
+
+ /**
+ * Current radius of the virtual cylinder.
+ * @type {number}
+ * @private
+ */
+ this._cylinderRadius = 0.0;
+
+ /**
+ * A line that specifies the current local, virtual y-Axis of this sensor, given in world coordinates.
+ * @type {x3dom.fields.Line}
+ * @private
+ */
+ this._yAxisLine = null;
+
+ /**
+ * Specifies whether we are currently using cylinder behavior or disk behavior.
+ * @type {boolean}
+ * @private
+ */
+ this._cylinderMode = true;
+
+ /**
+ * Current rotation that is produced by this cylinder sensor
+ * @type {x3dom.fields.Quaternion}
+ * @private
+ */
+ this._currentRotationAngle = 0.0;
+ },
+ {
+ //----------------------------------------------------------------------------------------------------------------------
+ // PUBLIC FUNCTIONS
+ //----------------------------------------------------------------------------------------------------------------------
+
+ /**
+ * This function returns the parent transformation of this node, combined with its current axisRotation
+ * @overrides x3dom.nodeTypes.X3DPointingDeviceSensorNode.getCurrentTransform
+ */
+ getCurrentTransform: function ()
+ {
+ var parentTransform = x3dom.nodeTypes.X3DDragSensorNode.prototype.getCurrentTransform.call(this);
+
+ return parentTransform.mult(this._rotationMatrix);
+ },
+
+ //----------------------------------------------------------------------------------------------------------------------
+ // PRIVATE FUNCTIONS
+ //----------------------------------------------------------------------------------------------------------------------
+
+ /**
+ * @overrides x3dom.nodeTypes.X3DDragSensorNode.prototype._startDragging
+ * @private
+ */
+ _startDragging: function(viewarea, x, y, wx, wy, wz)
+ {
+ x3dom.nodeTypes.X3DDragSensorNode.prototype._startDragging.call(this, viewarea, x, y, wx, wy, wz);
+
+ this._currentRotation = new x3dom.fields.Quaternion();
+
+ this._viewArea = viewarea;
+
+ //y axis line, in local sensor coordinates
+ this._yAxisLine = new x3dom.fields.Line(new x3dom.fields.SFVec3f(0.0, 0.0, 0.0),
+ new x3dom.fields.SFVec3f(0.0, 1.0, 0.0));
+
+ this._inverseToWorldMatrix = this.getCurrentTransform().inverse();
+
+ //compute initial cylinder intersection point, in local sensor coordinates
+ var firstIntersection = this._inverseToWorldMatrix.multMatrixPnt(new x3dom.fields.SFVec3f(wx, wy, wz));
+
+ //TODO: add disk mode
+
+ //compute distance between point of intersection and y-axis
+
+ var closestPointOnYAxis = this._yAxisLine.closestPoint(firstIntersection);
+
+ this._initialCylinderIntersectionVector = firstIntersection.subtract(closestPointOnYAxis);
+
+ this._cylinderRadius = this._initialCylinderIntersectionVector.length();
+
+ this._initialCylinderIntersectionVector = this._initialCylinderIntersectionVector.normalize();
+ },
+
+ //----------------------------------------------------------------------------------------------------------------------
+
+ /**
+ * @overrides x3dom.nodeTypes.X3DDragSensorNode._process2DDrag
+ * @private
+ */
+ _process2DDrag: function(x, y, dx, dy)
+ {
+ x3dom.nodeTypes.X3DDragSensorNode.prototype._process2DDrag.call(this, x, y, dx, dy);
+
+ //cylinder mode
+ if (this._cylinderMode)
+ {
+ //compute hit point on virtual cylinder geometry
+ var viewRay = this._viewArea.calcViewRay(x, y);
+
+ viewRay.pos = this._inverseToWorldMatrix.multMatrixPnt(viewRay.pos);
+ viewRay.dir = this._inverseToWorldMatrix.multMatrixVec(viewRay.dir);
+
+ //0. assume the following equation:
+ // At the point of intersection, the distance between the ray of sight and the cylinder equals
+ // the cylinder radius r.
+ // This means a ray parameter alpha must be found, so that the minimum distance between the point on
+ // the ray and the cylinder axis equals r:
+ // | ((S + alpha*V) - O) - Y*<(S + alpha*V) - O, Y> | = r
+ // with:
+ // | X | = length of vector X
+ // <X1, X2> = dot product of vectors X1, X2
+ // and variables
+ // alpha := Ray Parameter (should be found)
+ // S := Ray Origin
+ // V := Ray Direction
+ // O := Local Y-Axis Anchor Point
+ // Y := Local Y-Axis Direction
+
+ //1. bring equation into the following form:
+ // | alpha * A - B | = r
+ var A = viewRay.dir.subtract(this._yAxisLine.dir.multiply(viewRay.dir.dot(this._yAxisLine.dir)));
+ var B = viewRay.pos.subtract(this._yAxisLine.pos).add(this._yAxisLine.dir.multiply(
+ this._yAxisLine.dir.dot(this._yAxisLine.pos.subtract(viewRay.pos))));
+
+ //2. solve quadratic formula (0, 1 or 2 solutions are possible)
+ var p = 2 * A.dot(B) / A.dot(A);
+ var q = (B.dot(B) - this._cylinderRadius*this._cylinderRadius) / A.dot(A);
+
+ var sqrt_part = p*p*0.25 - q;
+
+ var alpha_1;
+ var alpha_2;
+
+ //is the cylinder hit?
+ if (sqrt_part >= 0)
+ {
+ sqrt_part = Math.sqrt(sqrt_part);
+ alpha_1 = -p*0.5 + sqrt_part;
+ alpha_2 = -p*0.5 - sqrt_part;
+
+ //if we are inside the cylinder, do nothing, otherwise pick the closest point of intersection
+ alpha_1 = Math.min(alpha_1, alpha_2);
+
+ if (alpha_1 > 0.0)
+ {
+ //TODO: output trackPoint_changed event
+ var hitPoint = viewRay.pos.add(viewRay.dir.multiply(alpha_1));
+
+ var closestPointOnYAxis = this._yAxisLine.closestPoint(hitPoint);
+
+ var vecToHitPoint = hitPoint.subtract(closestPointOnYAxis).normalize();
+
+ this._currentRotation = x3dom.fields.Quaternion.rotateFromTo(this._initialCylinderIntersectionVector, vecToHitPoint);
+
+ var offsetQuat = x3dom.fields.Quaternion.axisAngle(this._yAxisLine.dir, this._vf.offset);
+
+ this._currentRotation = this._currentRotation.multiply(offsetQuat);
+
+ //output rotationChanged_event, given in local sensor coordinates
+ this.postMessage('rotation_changed', this._currentRotation);
+ }
+ }
+ }
+ //disk mode
+ else
+ {
+ //TODO: implement
+ }
+ },
+
+ //----------------------------------------------------------------------------------------------------------------------
+
+ /**
+ * @overrides x3dom.nodeTypes.X3DDragSensorNode._stopDragging
+ * @private
+ */
+ _stopDragging: function()
+ {
+ x3dom.nodeTypes.X3DDragSensorNode.prototype._stopDragging.call(this);
+
+ if (this._vf.autoOffset)
+ {
+ this._vf.offset = this._currentRotation.angle();
+ this.postMessage('offset_changed', this._vf.offset);
+ }
+ }
+
+ //----------------------------------------------------------------------------------------------------------------------
+ }
+ )
+);
+
+x3dom.versionInfo = {
+ version: '1.6.2',
+ revision: '8f5655cec1951042e852ee9def292c9e0194186b',
+ date: 'Sat Dec 20 00:03:52 2014 +0100'
+};
+
+
+x3dom.versionInfo = {
+ version: '1.6.2',
+ revision: '8f5655cec1951042e852ee9def292c9e0194186b',
+ date: 'Sat Dec 20 00:03:52 2014 +0100'
+};
+
diff --git a/debian/patches/0001_jam.patch b/debian/patches/0001_jam.patch
new file mode 100644
index 0000000..b0469f3
--- /dev/null
+++ b/debian/patches/0001_jam.patch
@@ -0,0 +1,143 @@
+Description: Add multiarch support to jam files
+Author: Jörg Frings Fürst <debian@jff-webhosting.net>
+Forwarded: http://www.freelists.org/post/argyllcms/Some-buildsystem-issues
+Last-Update: 2015-08-23
+---
+This patch header follows DEP-3: http://dep.debian.net/deps/dep3/
+Index: trunk/Jambase
+===================================================================
+--- trunk.orig/Jambase
++++ trunk/Jambase
+@@ -941,7 +941,7 @@ else if $(UNIX)
+
+ # UNIX defaults
+
+- CCFLAGS ?= -DUNIX -D_THREAD_SAFE -pipe ;
++ CCFLAGS ?= $(CPPFLAGS) -g -DUNIX -D_THREAD_SAFE -pipe -fPIC ;
+ CCOPTFLAG ?= -O2 ;
+ CCDEBUGFLAG ?= -g ;
+ CCPROFFLAG ?= ;
+@@ -951,7 +951,7 @@ else if $(UNIX)
+ CHGRP ?= chgrp ;
+ CHOWN ?= chown ;
+ LEX ?= lex ;
+- LINKFLAGS ?= ;
++ LINKFLAGS ?= $(LDFLAGS) ;
+ LINKOPTFLAG ?= -O ; # Affects creating .so's
+ LINKSTRIPFLAG ?= -s ;
+ LINKDEBUGFLAG ?= ;
+@@ -1037,7 +1037,7 @@ else if $(UNIX)
+ RMDIR ?= $(RM) ;
+ RSH ?= rsh ;
+ SED ?= sed ;
+- SHELLHEADER ?= "#!/bin/sh" ;
++ SHELLHEADER ?= "#!/bin/bash" ;
+ SHELLMODE ?= 755 ;
+ SLASH ?= / ;
+ STDHDRS ?= /usr/include ;
+Index: trunk/Jamtop
+===================================================================
+--- trunk.orig/Jamtop
++++ trunk/Jamtop
+@@ -23,6 +23,7 @@ ANCHORED_PATH_VARS = DESTDIR ;
+ # Should we also allow CFLAGS, CXXFLAGS, CPPFLAGS & LDFLAGS env. variables
+ # to have effect ?
+
++BUILD_SHARED_LIB = 1 ;
+
+ # Tell standalone libraries that they are part of Argyll:
+ DEFINES += ARGYLLCMS ;
+@@ -152,17 +153,82 @@ rule CheckForLibrary {
+ }
+
+ if ! $(BUILTIN_$(UCASE)) && $(UNIX) {
+- if [ GLOB /usr/include$(subd) : $(lcase).h $(lcase)lib.h ]
+- || [ GLOB /usr/local/include$(subd) : $(lcase).h $(lcase)lib.h ]
+- || [ GLOB /usr/include/x86_64-linux-gnu$(subd) : $(lcase).h $(lcase)lib.h ]
+- || [ GLOB /usr/include/i386-linux-gnu$(subd) : $(lcase).h $(lcase)lib.h ] {
+- if [ GLOB /usr/lib : lib$(lcase).so ] || [ GLOB /usr/lib : lib$(lcase).a ]
+- || [ GLOB /usr/local/lib : lib$(lcase).so ] || [ GLOB /usr/local/lib : lib$(lcase).a ]
+- || [ GLOB /usr/lib64 : lib$(lcase).so ] || [ GLOB /usr/lib64 : lib$(lcase).a ]
+- || [ GLOB /usr/lib/x86_64-linux-gnu : lib$(lcase).so ]
+- || [ GLOB /usr/lib/x86_64-linux-gnu : lib$(lcase).a ]
+- || [ GLOB /usr/lib/i386-linux-gnu : lib$(lcase).so ]
+- || [ GLOB /usr/lib/i386-linux-gnu : lib$(lcase).a ] {
++ if [ GLOB /usr/include$(subd) : $(lcase).h $(lcase)lib.h ]
++ || [ GLOB /usr/local/include$(subd) : $(lcase).h $(lcase)lib.h ]
++ || [ GLOB /usr/include/x86_64-linux-gnu$(subd) : $(lcase).h $(lcase)lib.h ]
++ || [ GLOB /usr/include/i386-linux-gnu$(subd) : $(lcase).h $(lcase)lib.h ]
++ || [ GLOB /usr/include/alpha-linux-gnu$(subd) : $(lcase).h $(lcase)lib.h ]
++ || [ GLOB /usr/include/aarch64-linux-gnu$(subd) : $(lcase).h $(lcase)lib.h ]
++ || [ GLOB /usr/include/arm-linux-gnueabi$(subd) : $(lcase).h $(lcase)lib.h ]
++ || [ GLOB /usr/include/arm-linux-gnueabihf$(subd) : $(lcase).h $(lcase)lib.h ]
++ || [ GLOB /usr/include/hppa-linux-gnu$(subd) : $(lcase).h $(lcase)lib.h ]
++ || [ GLOB /usr/include/i386-gnu$(subd) : $(lcase).h $(lcase)lib.h ]
++ || [ GLOB /usr/include/x86_64-kfreebsd-gnu$(subd) : $(lcase).h $(lcase)lib.h ]
++ || [ GLOB /usr/include/i386-kfreebsd-gnu$(subd) : $(lcase).h $(lcase)lib.h ]
++ || [ GLOB /usr/include/m68k-linux-gnu$(subd) : $(lcase).h $(lcase)lib.h ]
++ || [ GLOB /usr/include/mips-linux-gnu$(subd) : $(lcase).h $(lcase)lib.h ]
++ || [ GLOB /usr/include/mipsel-linux-gnu$(subd) : $(lcase).h $(lcase)lib.h ]
++ || [ GLOB /usr/include/mips64el-linux-gnuabi64$(subd) : $(lcase).h $(lcase)lib.h ]
++ || [ GLOB /usr/include/powerpc-linux-gnu$(subd) : $(lcase).h $(lcase)lib.h ]
++ || [ GLOB /usr/include/powerpc-linux-gnuspe$(subd) : $(lcase).h $(lcase)lib.h ]
++ || [ GLOB /usr/include/powerpc64-linux-gnu$(subd) : $(lcase).h $(lcase)lib.h ]
++ || [ GLOB /usr/include/powerpc64le-linux-gnu$(subd) : $(lcase).h $(lcase)lib.h ]
++ || [ GLOB /usr/include/s390x-linux-gnu$(subd) : $(lcase).h $(lcase)lib.h ]
++ || [ GLOB /usr/include/sh4-linux-gnu$(subd) : $(lcase).h $(lcase)lib.h ]
++ || [ GLOB /usr/include/sparc-linux-gnu$(subd) : $(lcase).h $(lcase)lib.h ]
++ || [ GLOB /usr/include/sparc64-linux-gnu$(subd) : $(lcase).h $(lcase)lib.h ]
++ || [ GLOB /usr/include/x86_64-linux-gnux32$(subd) : $(lcase).h $(lcase)lib.h ] {
++ if [ GLOB /usr/lib : lib$(lcase).so ] || [ GLOB /usr/lib : lib$(lcase).a ]
++ || [ GLOB /usr/local/lib : lib$(lcase).so ]
++ || [ GLOB /usr/local/lib : lib$(lcase).a ]
++ || [ GLOB /usr/lib64 : lib$(lcase).so ]
++ || [ GLOB /usr/lib64 : lib$(lcase).a ]
++ || [ GLOB /usr/lib/x86_64-linux-gnu : lib$(lcase).so ]
++ || [ GLOB /usr/lib/x86_64-linux-gnu : lib$(lcase).a ]
++ || [ GLOB /usr/lib/i386-linux-gnu : lib$(lcase).so ]
++ || [ GLOB /usr/lib/i386-linux-gnu : lib$(lcase).a ]
++ || [ GLOB /usr/lib/alpha-linux-gnu : lib$(lcase).so ]
++ || [ GLOB /usr/lib/alpha-linux-gnu : lib$(lcase).a ]
++ || [ GLOB /usr/lib/aarch64-linux-gnu : lib$(lcase).so ]
++ || [ GLOB /usr/lib/aarch64-linux-gnu : lib$(lcase).a ]
++ || [ GLOB /usr/lib/arm-linux-gnueabi : lib$(lcase).so ]
++ || [ GLOB /usr/lib/arm-linux-gnueabi : lib$(lcase).a ]
++ || [ GLOB /usr/lib/arm-linux-gnueabihf : lib$(lcase).so ]
++ || [ GLOB /usr/lib/arm-linux-gnueabihf : lib$(lcase).a ]
++ || [ GLOB /usr/lib/hppa-linux-gnu : lib$(lcase).so ]
++ || [ GLOB /usr/lib/hppa-linux-gnu : lib$(lcase).a ]
++ || [ GLOB /usr/lib/i386-gnu : lib$(lcase).so ]
++ || [ GLOB /usr/lib/i386-gnu : lib$(lcase).a ]
++ || [ GLOB /usr/lib/x86_64-kfreebsd-gnu : lib$(lcase).so ]
++ || [ GLOB /usr/lib/x86_64-kfreebsd-gnu : lib$(lcase).a ]
++ || [ GLOB /usr/lib/i386-kfreebsd-gnu : lib$(lcase).so ]
++ || [ GLOB /usr/lib/i386-kfreebsd-gnu : lib$(lcase).a ]
++ || [ GLOB /usr/lib/m68k-linux-gnu : lib$(lcase).so ]
++ || [ GLOB /usr/lib/m68k-linux-gnu : lib$(lcase).a ]
++ || [ GLOB /usr/lib/mips-linux-gnu : lib$(lcase).so ]
++ || [ GLOB /usr/lib/mips-linux-gnu : lib$(lcase).a ]
++ || [ GLOB /usr/lib/mipsel-linux-gnu : lib$(lcase).so ]
++ || [ GLOB /usr/lib/mipsel-linux-gnu : lib$(lcase).a ]
++ || [ GLOB /usr/lib/mips64el-linux-gnuabi64 : lib$(lcase).so ]
++ || [ GLOB /usr/lib/mips64el-linux-gnuabi64 : lib$(lcase).a ]
++ || [ GLOB /usr/lib/powerpc-linux-gnu : lib$(lcase).so ]
++ || [ GLOB /usr/lib/powerpc-linux-gnu : lib$(lcase).a ]
++ || [ GLOB /usr/lib/powerpc-linux-gnuspe : lib$(lcase).so ]
++ || [ GLOB /usr/lib/powerpc-linux-gnuspe : lib$(lcase).a ]
++ || [ GLOB /usr/lib/powerpc64-linux-gnu : lib$(lcase).so ]
++ || [ GLOB /usr/lib/powerpc64-linux-gnu : lib$(lcase).a ]
++ || [ GLOB /usr/lib/powerpc64le-linux-gnu : lib$(lcase).so ]
++ || [ GLOB /usr/lib/powerpc64le-linux-gnu : lib$(lcase).a ]
++ || [ GLOB /usr/lib/s390x-linux-gnu : lib$(lcase).so ]
++ || [ GLOB /usr/lib/s390x-linux-gnu : lib$(lcase).a ]
++ || [ GLOB /usr/lib/sh4-linux-gnu : lib$(lcase).so ]
++ || [ GLOB /usr/lib/sh4-linux-gnu : lib$(lcase).a ]
++ || [ GLOB /usr/lib/sparc-linux-gnu : lib$(lcase).so ]
++ || [ GLOB /usr/lib/sparc-linux-gnu : lib$(lcase).a ]
++ || [ GLOB /usr/lib/sparc64-linux-gnu : lib$(lcase).so ]
++ || [ GLOB /usr/lib/sparc64-linux-gnu : lib$(lcase).a ]
++ || [ GLOB /usr/lib/x86_64-linux-gnux32 : lib$(lcase).so ]
++ || [ GLOB /usr/lib/x86_64-linux-gnux32 : lib$(lcase).a ] {
+ echo "Using system $(UCASE) library" ;
+ $(UCASE)LIB = ;
+ $(UCASE)INC = ;
diff --git a/debian/patches/0100_spelling.patch b/debian/patches/0100_spelling.patch
new file mode 100644
index 0000000..9797224
--- /dev/null
+++ b/debian/patches/0100_spelling.patch
@@ -0,0 +1,1720 @@
+Description: correct some typos
+Author: Jörg Frings-Fürst <debian@jff-webhosting.net>
+Last-Update: 2017-08-26
+---
+This patch header follows DEP-3: http://dep.debian.net/deps/dep3/
+Index: trunk/spectro/dispcal.c
+===================================================================
+--- trunk.orig/spectro/dispcal.c
++++ trunk/spectro/dispcal.c
+@@ -1985,7 +1985,7 @@ int main(int argc, char *argv[]) {
+ /* Serial port flow control */
+ } else if (argv[fa][1] == 'W') {
+ fa = nfa;
+- if (na == NULL) usage(0,"Paramater expected following -W");
++ if (na == NULL) usage(0,"Parameter expected following -W");
+ if (na[0] == 'n' || na[0] == 'N')
+ fc = fc_None;
+ else if (na[0] == 'h' || na[0] == 'H')
+@@ -2008,13 +2008,13 @@ int main(int argc, char *argv[]) {
+ /* Black point correction amount */
+ } else if (argv[fa][1] == 'k') {
+ fa = nfa;
+- if (na == NULL) usage(0,"Paramater expected following -k");
++ if (na == NULL) usage(0,"Parameter expected following -k");
+ bkcorrect = atof(na);
+ if (bkcorrect < 0.0 || bkcorrect > 1.0) usage(0,"-k parameter must be between 0.0 and 1.0");
+ /* Neutral blend rate (power) */
+ } else if (argv[fa][1] == 'A') {
+ fa = nfa;
+- if (na == NULL) usage(0,"Paramater expected following -A");
++ if (na == NULL) usage(0,"Parameter expected following -A");
+ x.nbrate = atof(na);
+ if (x.nbrate < 0.05 || x.nbrate > 20.0) usage(0,"-A parameter must be between 0.05 and 20.0");
+ /* Black brightness */
+@@ -2047,7 +2047,7 @@ int main(int argc, char *argv[]) {
+ /* COM port */
+ } else if (argv[fa][1] == 'c') {
+ fa = nfa;
+- if (na == NULL) usage(0,"Paramater expected following -c");
++ if (na == NULL) usage(0,"Parameter expected following -c");
+ comport = atoi(na);
+ if (comport < 1 || comport > 50) usage(0,"-c parameter %d out of range",comport);
+
+@@ -3111,7 +3111,7 @@ int main(int argc, char *argv[]) {
+ /* Black level adjustment */
+ /* Due to the possibility of the channel offsets not being even, */
+ /* we use the largest of the XYZ values after they have been */
+- /* scaled to be even acording to the white XYZ balance. */
++ /* scaled to be even according to the white XYZ balance. */
+ /* It's safer to set the black level a bit low, and then the */
+ /* calibration curves can bump the low ones up. */
+ if (c == '1') {
+Index: trunk/spectro/spotread.c
+===================================================================
+--- trunk.orig/spectro/spotread.c
++++ trunk/spectro/spotread.c
+@@ -509,7 +509,7 @@ int main(int argc, char *argv[]) {
+ /* COM port */
+ } else if (argv[fa][1] == 'c') {
+ fa = nfa;
+- if (na == NULL) usage("Paramater expected following -c");
++ if (na == NULL) usage("Parameter expected following -c");
+ {
+ comport = atoi(na);
+ if (comport < 1 || comport > 40) usage("-c parameter %d out of range",comport);
+@@ -518,7 +518,7 @@ int main(int argc, char *argv[]) {
+ /* Display type */
+ } else if (argv[fa][1] == 'y') {
+ fa = nfa;
+- if (na == NULL) usage("Paramater expected following -y");
++ if (na == NULL) usage("Parameter expected following -y");
+ dtype = na[0];
+
+ #ifndef SALONEINSTLIB
+@@ -526,7 +526,7 @@ int main(int argc, char *argv[]) {
+ } else if (argv[fa][1] == 'I') {
+
+ fa = nfa;
+- if (na == NULL) usage("Paramater expected following -I");
++ if (na == NULL) usage("Parameter expected following -I");
+ if (strcmp(na, "A") == 0
+ || strcmp(na, "M0") == 0) {
+ tillum_set = spec = 1;
+@@ -574,7 +574,7 @@ int main(int argc, char *argv[]) {
+ /* Spectral Illuminant type for XYZ computation */
+ } else if (argv[fa][1] == 'i') {
+ fa = nfa;
+- if (na == NULL) usage("Paramater expected following -i");
++ if (na == NULL) usage("Parameter expected following -i");
+ if (strcmp(na, "A") == 0) {
+ illum_set = spec = 1;
+ illum = icxIT_A;
+@@ -623,7 +623,7 @@ int main(int argc, char *argv[]) {
+ /* Spectral Observer type */
+ } else if (argv[fa][1] == 'Q') {
+ fa = nfa;
+- if (na == NULL) usage("Paramater expected following -Q");
++ if (na == NULL) usage("Parameter expected following -Q");
+ if (strcmp(na, "1931_2") == 0) { /* Classic 2 degree */
+ obType = icxOT_CIE_1931_2;
+ } else if (strcmp(na, "1964_10") == 0) { /* Classic 10 degree */
+@@ -712,7 +712,7 @@ int main(int argc, char *argv[]) {
+ /* Filter configuration */
+ } else if (argv[fa][1] == 'F') {
+ fa = nfa;
+- if (na == NULL) usage("Paramater expected following -F");
++ if (na == NULL) usage("Parameter expected following -F");
+ if (na[0] == 'n' || na[0] == 'N')
+ fe = inst_opt_filter_none;
+ else if (na[0] == 'p' || na[0] == 'P')
+@@ -727,13 +727,13 @@ int main(int argc, char *argv[]) {
+ /* Extra filter compensation file */
+ } else if (argv[fa][1] == 'E') {
+ fa = nfa;
+- if (na == NULL) usage("Paramater expected following -E");
++ if (na == NULL) usage("Parameter expected following -E");
+ strncpy(filtername,na,MAXNAMEL-1); filtername[MAXNAMEL-1] = '\000';
+
+ /* XRGA conversion */
+ } else if (argv[fa][1] == 'A') {
+ fa = nfa;
+- if (na == NULL) usage("Paramater expected following -A");
++ if (na == NULL) usage("Parameter expected following -A");
+ if (na[0] == 'N')
+ calstd = xcalstd_none;
+ else if (na[0] == 'A')
+@@ -743,7 +743,7 @@ int main(int argc, char *argv[]) {
+ else if (na[0] == 'G')
+ calstd = xcalstd_gmdi;
+ else
+- usage("Paramater after -A '%c' not recognized",na[0]);
++ usage("Parameter after -A '%c' not recognized",na[0]);
+
+ /* Show Yxy */
+ } else if (argv[fa][1] == 'x') {
+@@ -1479,7 +1479,7 @@ int main(int argc, char *argv[]) {
+
+ /* Or something is wrong with instrument capabilities */
+ } else {
+- printf("\nNo reasonable trigger mode avilable for this instrument\n");
++ printf("\nNo reasonable trigger mode available for this instrument\n");
+ it->del(it);
+ return -1;
+ }
+Index: trunk/spectro/colorhug.c
+===================================================================
+--- trunk.orig/spectro/colorhug.c
++++ trunk/spectro/colorhug.c
+@@ -213,7 +213,7 @@ colorhug_command(colorhug *p,
+
+ a1logd(p->log,8,"colorhug_command: Read %d bytes and %d read\n",xrbytes,rbytes);
+ if (rbytes >= 2) {
+- a1logd(p->log,6,"colorhug_command: recieved cmd '%s' error '%s' args '%s'\n",
++ a1logd(p->log,6,"colorhug_command: received cmd '%s' error '%s' args '%s'\n",
+ inst_desc(buf[1]),
+ colorhug_interp_error((inst *) p, buf[0]),
+ icoms_tohex(buf, rbytes - 2));
+Index: trunk/spectro/dispwin.c
+===================================================================
+--- trunk.orig/spectro/dispwin.c
++++ trunk/spectro/dispwin.c
+@@ -5234,7 +5234,7 @@ int ddebug /* >0 to print debug sta
+ vinfo = XGetVisualInfo(p->mydisplay, VisualIDMask, &template, &nitems);
+
+ if (nitems < 1) {
+- debugr2((errout,"new_dispwin: Failed to get XGetVisualInfo of defalt Visual\n"));
++ debugr2((errout,"new_dispwin: Failed to get XGetVisualInfo of default Visual\n"));
+ dispwin_del(p);
+ return NULL;
+ }
+Index: trunk/spectro/dtp51.c
+===================================================================
+--- trunk.orig/spectro/dtp51.c
++++ trunk/spectro/dtp51.c
+@@ -681,7 +681,7 @@ dtp51_interp_error(inst *pp, int ec) {
+ case DTP51_INVALID_STEP:
+ return "Invalid step";
+ case DTP51_NO_DATA_AVAILABLE:
+- return "No data availble";
++ return "No data available";
+ case DTP51_LAMP_MARGINAL:
+ return "Lamp marginal";
+ case DTP51_LAMP_FAILURE:
+Index: trunk/spectro/dtp92.c
+===================================================================
+--- trunk.orig/spectro/dtp92.c
++++ trunk/spectro/dtp92.c
+@@ -358,7 +358,7 @@ dtp92_init_coms(inst *pp, baud_rate br,
+ if ((ev = dtp92_command(p, tbuf, buf, MAX_MES_SIZE, 6.0)) != inst_ok)
+ error("Writing offset drift value failed");
+ else
+- printf("Writing offset drift value suceeded!\n");
++ printf("Writing offset drift value succeeded!\n");
+ } else {
+ printf("No command written\n");
+ }
+@@ -372,7 +372,7 @@ dtp92_init_coms(inst *pp, baud_rate br,
+ return inst_coms_fail;
+ }
+
+- a1logd(p->log, 2, "dtp92_init_coms: init coms has suceeded\n");
++ a1logd(p->log, 2, "dtp92_init_coms: init coms has succeeded\n");
+
+ p->gotcoms = 1;
+ return inst_ok;
+@@ -942,7 +942,7 @@ dtp92_interp_error(inst *pp, int ec) {
+ case DTP92_NO_DATA_AVAILABLE:
+ return "No data available";
+ case DTP92_MISSING_PARAMETER:
+- return "Paramter is missing";
++ return "Parameter is missing";
+ case DTP92_CALIBRATION_DENIED:
+ return "Invalid calibration enable code";
+ case DTP92_NEEDS_OFFSET_CAL:
+Index: trunk/spectro/hidio.c
+===================================================================
+--- trunk.orig/spectro/hidio.c
++++ trunk/spectro/hidio.c
+@@ -742,7 +742,7 @@ icoms_hid_read(icoms *p,
+ {
+ unsigned char *rbuf2;
+
+- /* Create a copy of the data recieved with one more byte */
++ /* Create a copy of the data received with one more byte */
+ if ((rbuf2 = malloc(bsize + 1)) == NULL) {
+ a1loge(p->log, ICOM_SYS, "icoms_hid_read: malloc failed\n");
+ return ICOM_SYS;
+Index: trunk/spectro/huey.c
+===================================================================
+--- trunk.orig/spectro/huey.c
++++ trunk/spectro/huey.c
+@@ -82,7 +82,7 @@ static int icoms2huey_err(int se, int to
+ /* i1Display command codes */
+ /* B = byte (8bit), S = short (16bit), W = word (32bit), A = string */
+ /* U = unused byte, - = no arguments/results */
+-/* The is a 7 byte command buffer and 6 response recieve buffer. */
++/* The is a 7 byte command buffer and 6 response receive buffer. */
+ /* :2 means the read is from a second 8 byte ep x81 read. */
+ /* cbuf[-] is command byte */
+ /* rbuf[-2] is continuation byte */
+Index: trunk/spectro/i1pro_imp.c
+===================================================================
+--- trunk.orig/spectro/i1pro_imp.c
++++ trunk/spectro/i1pro_imp.c
+@@ -2701,7 +2701,7 @@ int *pinstmsec) { /* Return instrument l
+ break;
+ }
+
+- a1logd(p->log, 2, "i1pro_meas_delay: stoped at sample %d time %f\n",i,samp[i].sec);
++ a1logd(p->log, 2, "i1pro_meas_delay: stopped at sample %d time %f\n",i,samp[i].sec);
+
+ /* Compute overall delay */
+ dispmsec = (int)(samp[i].sec * 1000.0 + 0.5); /* Display update time */
+@@ -3154,7 +3154,7 @@ i1pro_code i1pro_imp_measure(
+ }
+ }
+
+- a1logd(p->log,3,"i1pro_imp_measure sucessful return\n");
++ a1logd(p->log,3,"i1pro_imp_measure successful return\n");
+ if (user_trig)
+ return I1PRO_USER_TRIG;
+ return ev;
+@@ -3816,7 +3816,7 @@ i1pro_code i1pro_imp_meas_refrate(
+ }
+ }
+ } else {
+- a1logd(p->log, 3, "Not enough tries suceeded to determine refresh rate\n");
++ a1logd(p->log, 3, "Not enough tries succeeded to determine refresh rate\n");
+ }
+
+ return I1PRO_RD_NOREFR_FOUND;
+@@ -3911,7 +3911,7 @@ i1pro_code i1pro_restore_refspot_cal(i1p
+ return I1PRO_OK;
+ }
+
+- /* We've sucessfully restored the dark calibration */
++ /* We've successfully restored the dark calibration */
+ s->dark_valid = 1;
+ s->ddate = m->caldate;
+
+@@ -3956,7 +3956,7 @@ i1pro_code i1pro_restore_refspot_cal(i1p
+ return I1PRO_OK;
+ }
+
+- /* We've sucessfully restored the calibration */
++ /* We've successfully restored the calibration */
+ s->cal_valid = 1;
+ s->cfdate = m->caldate;
+
+@@ -4312,7 +4312,7 @@ i1pro_code i1pro_save_calibration(i1pro
+ write_doubles(&x, fp, s->idark_data[3]-1, m->nraw+1);
+ }
+
+- a1logd(p->log,3,"nbytes = %d, Checkum = 0x%x\n",x.nbytes,x.chsum);
++ a1logd(p->log,3,"nbytes = %d, Checksum = 0x%x\n",x.nbytes,x.chsum);
+ write_ints(&x, fp, (int *)&x.chsum, 1);
+
+ if (fclose(fp) != 0)
+Index: trunk/spectro/madvrwin.c
+===================================================================
+--- trunk.orig/spectro/madvrwin.c
++++ trunk/spectro/madvrwin.c
+@@ -603,7 +603,7 @@ int ii = 0;
+ }
+ #endif
+
+- debugr("new_madvrwin: return sucessfully\n");
++ debugr("new_madvrwin: return successfully\n");
+
+ return p;
+ }
+Index: trunk/spectro/ss.c
+===================================================================
+--- trunk.orig/spectro/ss.c
++++ trunk/spectro/ss.c
+@@ -375,7 +375,7 @@ ss_init_coms(inst *pp, baud_rate br, flo
+
+ p->gotcoms = 1;
+
+- a1logd(p->log, 2, "ss_init_coms: init coms has suceeded\n");
++ a1logd(p->log, 2, "ss_init_coms: init coms has succeeded\n");
+
+ return inst_ok;
+ }
+@@ -1744,7 +1744,7 @@ ss_interp_error(inst *pp, int ec) {
+ case ss_et_FilterOutOfPos:
+ return "Filter wheel out of position";
+ case ss_et_SendTimeout:
+- return "Data transmission timout";
++ return "Data transmission timeout";
+ case ss_et_DriveError:
+ return "Data drive defect";
+ case ss_et_MeasDisabled:
+@@ -1864,7 +1864,7 @@ ss_interp_error(inst *pp, int ec) {
+ case ss_et_BadHexEncoding:
+ return "Message received from instrument has bad Hex encoding";
+ case ss_et_RecBufferOverun:
+- return "Message received from instrument would overflow recieve buffer";
++ return "Message received from instrument would overflow receive buffer";
+ default:
+ return "Unknown error code";
+ }
+Index: trunk/spectro/ss_imp.c
+===================================================================
+--- trunk.orig/spectro/ss_imp.c
++++ trunk/spectro/ss_imp.c
+@@ -217,7 +217,7 @@ static int h2b(ss *p, char c) {
+ return 0;
+ }
+
+-/* Return the first enum from the recieve buffer without removing it. */
++/* Return the first enum from the receive buffer without removing it. */
+ int ss_peek_ans(ss *p) {
+ int rv;
+
+Index: trunk/spectro/webwin.c
+===================================================================
+--- trunk.orig/spectro/webwin.c
++++ trunk/spectro/webwin.c
+@@ -411,7 +411,7 @@ int ddebug /* >0 to print debug sta
+ msec_sleep(50);
+ }
+
+- debugr("new_webwin: return sucessfully\n");
++ debugr("new_webwin: return successfully\n");
+
+ return p;
+ }
+Index: trunk/xicc/cv.c
+===================================================================
+--- trunk.orig/xicc/cv.c
++++ trunk/xicc/cv.c
+@@ -101,7 +101,7 @@ main(int argc, char *argv[]) {
+
+ printf("There are %d parameters:\n",np); fflush(stdout);
+ for (i = 0; i < np; i++) {
+- printf("Paramter %d = %f\n",i, params[i]); fflush(stdout);
++ printf("Parameter %d = %f\n",i, params[i]); fflush(stdout);
+ }
+
+ /* Display the result */
+Index: trunk/spectro/ss_imp.h
+===================================================================
+--- trunk.orig/spectro/ss_imp.h
++++ trunk/spectro/ss_imp.h
+@@ -723,7 +723,7 @@ void ss_add_string(struct _ss *p, char *
+ /* - - - - - - - - - - - - - - - - - - - - - */
+ /* ANSWER: */
+
+-/* Return the first enum from the recieve buffer without removing it. */
++/* Return the first enum from the receive buffer without removing it. */
+ int ss_peek_ans(struct _ss *p);
+
+ /* Remove a Spectrolino answer enum from the revieve buffer, */
+Index: trunk/imdi/cctiff.c
+===================================================================
+--- trunk.orig/imdi/cctiff.c
++++ trunk/imdi/cctiff.c
+@@ -36,7 +36,7 @@
+ Add flag to ignore inkname mismatches.
+
+
+- Should add support for transfering any extra alpha
++ Should add support for transferring any extra alpha
+ planes from input to output, rather than simply ignoring them.
+
+
+@@ -1952,11 +1952,11 @@ main(int argc, char *argv[]) {
+
+ if (wh != NULL) {
+ printf("Output TIFF file '%s'\n",out_name);
+- printf("Ouput raster file ICC colorspace is %s\n",icm2str(icmColorSpaceSignature,su.outs));
++ printf("Output raster file ICC colorspace is %s\n",icm2str(icmColorSpaceSignature,su.outs));
+ printf("Output TIFF file photometric is %s\n",Photometric2str(wphotometric));
+ } else {
+ printf("Output JPEG file '%s'\n",out_name);
+- printf("Ouput raster file ICC colorspace is %s\n",icm2str(icmColorSpaceSignature,su.outs));
++ printf("Output raster file ICC colorspace is %s\n",icm2str(icmColorSpaceSignature,su.outs));
+ printf("Output JPEG file colorspace is %s\n",JPEG_cspace2str(wj.jpeg_color_space));
+ if (wdesc != NULL)
+ printf("Output raster file description: '%s'\n",wdesc);
+Index: trunk/imdi/imdi.h
+===================================================================
+--- trunk.orig/imdi/imdi.h
++++ trunk/imdi/imdi.h
+@@ -38,7 +38,7 @@ struct _imdi {
+
+ /* Note that once an imdi is created, multiple can call interp() without */
+ /* interfering with each other, allowing parallel execution. */
+- void (*interp)(struct _imdi *s, void **outp, int outst, /* Ouput pointers and stride */
++ void (*interp)(struct _imdi *s, void **outp, int outst, /* Output pointers and stride */
+ void **inp, int inst, /* Input pointers and stride */
+ unsigned int npixels); /* Number of pixels */
+
+Index: trunk/spectro/munki_imp.c
+===================================================================
+--- trunk.orig/spectro/munki_imp.c
++++ trunk/spectro/munki_imp.c
+@@ -1905,7 +1905,7 @@ int *pinstmsec) { /* Return instrumen
+ break;
+ }
+
+- a1logd(p->log, 2, "munki_meas_delay: stoped at sample %d time %f\n",i,samp[i].sec);
++ a1logd(p->log, 2, "munki_meas_delay: stopped at sample %d time %f\n",i,samp[i].sec);
+
+ /* Compute overall delay and subtract patch change delay */
+ dispmsec = (int)(samp[i].sec * 1000.0 + 0.5);
+@@ -2386,7 +2386,7 @@ munki_code munki_imp_measure(
+ if (nvals > 0)
+ vals[0].duration = duration; /* Possible flash duration */
+
+- a1logd(p->log,3,"munki_imp_measure sucessful return\n");
++ a1logd(p->log,3,"munki_imp_measure successful return\n");
+ if (user_trig)
+ return MUNKI_USER_TRIG;
+ return ev;
+@@ -3047,7 +3047,7 @@ munki_code munki_imp_meas_refrate(
+ }
+ }
+ } else {
+- a1logd(p->log, 3, "Not enough tries suceeded to determine refresh rate\n");
++ a1logd(p->log, 3, "Not enough tries succeeded to determine refresh rate\n");
+ }
+
+ return MUNKI_RD_NOREFR_FOUND;
+@@ -3262,7 +3262,7 @@ munki_code munki_save_calibration(munki
+ write_doubles(&x, fp, s->idark_data[3]-1, m->nraw+1);
+ }
+
+- a1logd(p->log,3,"Checkum = 0x%x\n",x.chsum);
++ a1logd(p->log,3,"Checksum = 0x%x\n",x.chsum);
+ write_ints(&x, fp, (int *)&x.chsum, 1);
+
+ if (fclose(fp) != 0)
+@@ -6429,7 +6429,7 @@ munki_code munki_create_hr(munki *p, int
+ int i, j, jj, k, cx, sx;
+ munki_fc coeff[40][16]; /* Existing filter cooefficients */
+ int nwav1; /* Number of filters */
+- double wl_short1, wl_long1; /* Ouput wavelength of first and last filters */
++ double wl_short1, wl_long1; /* Output wavelength of first and last filters */
+ double wl_step1;
+ munki_xp xp[41]; /* Crossover points each side of filter */
+ munki_code ev = MUNKI_OK;
+@@ -8737,7 +8737,7 @@ munki_readmeasurement(
+
+ top = extra + m->c_inttime * nmeas;
+
+- a1logd(p->log,2,"munki_readmeasurement: inummeas %d, scanflag %d, address %p bsize 0x%x, timout %f\n",inummeas, scanflag, buf, bsize, top);
++ a1logd(p->log,2,"munki_readmeasurement: inummeas %d, scanflag %d, address %p bsize 0x%x, timeout %f\n",inummeas, scanflag, buf, bsize, top);
+
+ for (;;) {
+ int size; /* number of bytes to read */
+Index: trunk/target/printtarg.c
+===================================================================
+--- trunk.orig/target/printtarg.c
++++ trunk/target/printtarg.c
+@@ -345,7 +345,7 @@ static void ps_setcolor(trend *ss, xcal
+ } else if (c->altrep == 6) { /* DeviceN */
+ gen_ncolor(s, c);
+ } else {
+- error("Device white encoding not approproate!");
++ error("Device white encoding not appropriate!");
+ }
+
+ } else if (c->nmask == ICX_K) {
+@@ -362,7 +362,7 @@ static void ps_setcolor(trend *ss, xcal
+ } else if (c->altrep == 3) { /* DeviceN */
+ gen_ncolor(s, c);
+ } else {
+- error("Device black encoding not approproate!");
++ error("Device black encoding not appropriate!");
+ }
+
+ } else if (c->nmask == ICX_CMY) {
+@@ -377,7 +377,7 @@ static void ps_setcolor(trend *ss, xcal
+ } else if (c->altrep == 8) { /* DeviceN */
+ gen_ncolor(s, c);
+ } else {
+- error("Device CMY encoding not approproate!");
++ error("Device CMY encoding not appropriate!");
+ }
+
+ } else if (c->nmask == ICX_RGB || c->nmask == ICX_IRGB) {
+@@ -749,7 +749,7 @@ static void tiff_setcolor(trend *ss, xca
+ } else if (c->altrep == 6) { /* DeviceN single channel */
+ s->c[0] = cdev[0];
+ } else {
+- error("Device white encoding not approproate!");
++ error("Device white encoding not appropriate!");
+ }
+
+ } else if (c->nmask == ICX_K) {
+@@ -765,7 +765,7 @@ static void tiff_setcolor(trend *ss, xca
+ } else if (c->altrep == 3) { /* DeviceN single channel */
+ s->c[0] = cdev[0];
+ } else {
+- error("Device black encoding not approproate!");
++ error("Device black encoding not appropriate!");
+ }
+
+ } else if (c->nmask == ICX_CMY) {
+@@ -783,7 +783,7 @@ static void tiff_setcolor(trend *ss, xca
+ s->c[1] = cdev[1];
+ s->c[2] = cdev[2];
+ } else {
+- error("Device CMY encoding not approproate!");
++ error("Device CMY encoding not appropriate!");
+ }
+
+ } else {
+@@ -968,7 +968,7 @@ static trend *new_tiff_trend(
+ nc = icx_noofinks(nmask);
+ nc = 1;
+ } else {
+- error("Device white encoding not approproate");
++ error("Device white encoding not appropriate");
+ }
+
+ } else if (nmask == ICX_K) {
+@@ -984,7 +984,7 @@ static trend *new_tiff_trend(
+ nc = icx_noofinks(nmask);
+ nc = 1;
+ } else {
+- error("Device black encoding not approproate");
++ error("Device black encoding not appropriate");
+ }
+
+ } else if (nmask == ICX_RGB || nmask == ICX_IRGB) {
+@@ -1002,7 +1002,7 @@ static trend *new_tiff_trend(
+ csp = ncol_2d;
+ nc = icx_noofinks(nmask);
+ } else {
+- error("Device CMY encoding not approproate");
++ error("Device CMY encoding not appropriate");
+ }
+
+ } else if (nmask == ICX_CMYK) {
+@@ -2209,7 +2209,7 @@ int *p_npat /* Return number of patche
+
+
+ } else {
+- error("Unsupported intrument type");
++ error("Unsupported instrument type");
+ }
+
+ /* Compute page limits */
+@@ -2255,7 +2255,7 @@ int *p_npat /* Return number of patche
+ tidpad = (pprow - tidminp)/2; /* Center TID */
+
+ if (pprow < (1+nextrap))
+- error("Paper size not long enought for a single patch per row!");
++ error("Paper size not long enough for a single patch per row!");
+
+ *ptpprow = tpprow = pprow - nextrap; /* Test sample patches per row */
+
+@@ -2953,7 +2953,7 @@ char *argv[];
+ double sscale = 1.0; /* Spacer size scale */
+ int rand = 1;
+ int qbits = 0; /* Quantization bits */
+- int oft = 0; /* Ouput File type, 0 = PS, 1 = EPS , 2 = TIFF */
++ int oft = 0; /* Output File type, 0 = PS, 1 = EPS , 2 = TIFF */
+ int nocups = 0; /* Supress CUPS PS/EPS job ticket */
+ depth2d tiffdpth = bpc8_2d; /* TIFF pixel depth */
+ double tiffres = 100.0; /* TIFF resolution in DPI */
+Index: trunk/gamut/nearsmth.c
+===================================================================
+--- trunk.orig/gamut/nearsmth.c
++++ trunk/gamut/nearsmth.c
+@@ -263,7 +263,7 @@ double dxratio /* Depth expansion ratio
+ double va, vr, vd, vv = 0.0;
+
+ /* Absolute, Delta E^2 between test point and destination closest */
+- /* aodv is already positioned acording to the LCh weights, */
++ /* aodv is already positioned according to the LCh weights, */
+ /* so weight as per average of these */
+ a_o = w->a.o;
+ va = wdesq(dtp, aodv, a_o, a_o, a_o, SUM_POW);
+@@ -4067,7 +4067,7 @@ static void create_influence_plot(nearsm
+ swdiag = new_rspl(RSPL_NOFLAGS, 3, 3); /* Allocate 3D -> 3D */
+ swdiag->fit_rspl(swdiag, RSPL_NOFLAGS, fpnts, nmpts, NULL, NULL, gres, NULL, NULL, 1.0, avgdev, NULL);
+
+- /* Now create a plot of the sci_gam with the vertexes colored acording to the */
++ /* Now create a plot of the sci_gam with the vertexes colored according to the */
+ /* diagnostic map. */
+ if ((wrl = new_vrml("sci_gam_wt", 1, vrml_lab)) == NULL) {
+ fprintf(stderr,"gamut map: new_vrml failed for '%s%s'\n","sci_gam_wt",vrm_ext());
+Index: trunk/gamut/nearsmth.h
+===================================================================
+--- trunk.orig/gamut/nearsmth.h
++++ trunk/gamut/nearsmth.h
+@@ -294,7 +294,7 @@ gammapweights *src2, double wgt2,
+ gammapweights *src3, double wgt3
+ );
+
+-/* Tweak weights acording to extra cmy cusp flags or rel override */
++/* Tweak weights according to extra cmy cusp flags or rel override */
+ void tweak_weights(gammapweights out[14], int dst_cmymap, int rel_oride);
+
+ #endif /* NEARSMTH_H */
+Index: trunk/imdi/cctiffo.c
+===================================================================
+--- trunk.orig/imdi/cctiffo.c
++++ trunk/imdi/cctiffo.c
+@@ -307,7 +307,7 @@ int pmtc
+ case PHOTOMETRIC_LOGLUV:
+ return "CIELog2Luv";
+ }
+- sprintf(buf,"Unknonw Tag %d",pmtc);
++ sprintf(buf,"Unknown Tag %d",pmtc);
+ return buf;
+ }
+
+Index: trunk/imdi/greytiff.c
+===================================================================
+--- trunk.orig/imdi/greytiff.c
++++ trunk/imdi/greytiff.c
+@@ -131,7 +131,7 @@ int pmtc
+ case PHOTOMETRIC_LOGLUV:
+ return "CIELog2Luv";
+ }
+- sprintf(buf,"Unknonw Tag %d",pmtc);
++ sprintf(buf,"Unknown Tag %d",pmtc);
+ return buf;
+ }
+
+Index: trunk/link/collink.c
+===================================================================
+--- trunk.orig/link/collink.c
++++ trunk/link/collink.c
+@@ -1122,7 +1122,7 @@ void devip_devop(void *cntx, double *out
+ }
+ /* We've got the input profile PCS' at this point. */
+
+- /* If we're transfering the K value from the input profile to the */
++ /* If we're transferring the K value from the input profile to the */
+ /* output, copy it into locus[], which will be given to the inverse */
+ /* lookup function, else the inverse lookup will generate a K using */
+ /* the curve parameters. */
+@@ -1204,7 +1204,7 @@ void devip_devop(void *cntx, double *out
+ if (p->verb)
+ #endif
+ {
+- printf("White point hack mapped %f %f %f to %f %f %f, hit withing %f\n",
++ printf("White point hack mapped %f %f %f to %f %f %f, hit within %f\n",
+ p->in.wp[0],p->in.wp[1],p->in.wp[2],pcsv[0], pcsv[1], pcsv[2],dd);
+ fflush(stdout);
+ }
+@@ -1254,7 +1254,7 @@ void devip_devop(void *cntx, double *out
+ if (p->nhack == 2) {
+ /* Ideally we would create a 4D PCSK -> PCSK gamut mapping */
+ /* to smoothly and accurately cope with the changing source */
+- /* and destination gamuts acording to their degree of "K onlyness". */
++ /* and destination gamuts according to their degree of "K onlyness". */
+ /* In practice we're going to simply interpolated between */
+ /* two extremes: unrestricted gamut and K only black gamut. */
+ double map0[3], map1[3];
+@@ -4807,7 +4807,7 @@ main(int argc, char *argv[]) {
+ }
+
+ if (li.verb)
+- printf("Finished verfication\n");
++ printf("Finished verification\n");
+
+ printf("Average error = %f%%, peak error = %f%%\n",aerr * 100.0/nerr, perr * 100.0);
+ printf("Input %f %f %f %f\n",pin[0], pin[1], pin[2], pin[3]);
+Index: trunk/profile/printcal.c
+===================================================================
+--- trunk.orig/profile/printcal.c
++++ trunk/profile/printcal.c
+@@ -1294,7 +1294,7 @@ int main(int argc, char *argv[]) {
+ icmXYZ2Lab(&wht, wp->Lab, wp->XYZ);
+ }
+
+- /* Sort the channel acording to device value */
++ /* Sort the channel according to device value */
+ /* For a consistent result for identical device values, */
+ /* secondary sort by inverse CIE value */
+ //#define HEAP_COMPARE(A,B) ((A).dev < (B).dev)
+Index: trunk/spectro/dispsup.c
+===================================================================
+--- trunk.orig/spectro/dispsup.c
++++ trunk/spectro/dispsup.c
+@@ -721,7 +721,7 @@ static int disprd_read_imp(
+ scb->serno = p->serno++;
+ scb->msec = msec_time();
+
+- a1logd(p->log,1, "got reading %f %f %f, transfering to col\n",
++ a1logd(p->log,1, "got reading %f %f %f, transferring to col\n",
+ val.XYZ[0], val.XYZ[1], val.XYZ[2]);
+
+ scb->mtype = val.mtype;
+@@ -1241,7 +1241,7 @@ int disprd_ambient(
+
+ /* Or something is wrong with instrument capabilities */
+ } else {
+- printf("No reasonable trigger mode avilable for this instrument\n");
++ printf("No reasonable trigger mode available for this instrument\n");
+ return 2;
+ }
+
+@@ -2344,7 +2344,7 @@ static int config_inst_displ(disprd *p)
+ /* Reset key meanings */
+ inst_reset_uih();
+
+- a1logd(p->log,1,"config_inst_displ suceeded\n");
++ a1logd(p->log,1,"config_inst_displ succeeded\n");
+ return 0;
+ }
+
+Index: trunk/gamut/gammap.c
+===================================================================
+--- trunk.orig/gamut/gammap.c
++++ trunk/gamut/gammap.c
+@@ -859,7 +859,7 @@ gammap *new_gammap(
+ #endif
+ if (gmi->bph == gmm_clipBP) {
+
+- /* Extend the target black point to accomodate the */
++ /* Extend the target black point to accommodate the */
+ /* bent or clipped destination space L* range */
+ if (fabp[0] < dr_cs_bp[0]) {
+ t = (fabp[0] - dr_cs_wp[0])/(dr_cs_bp[0] - dr_cs_wp[0]);
+Index: trunk/profile/profout.c
+===================================================================
+--- trunk.orig/profile/profout.c
++++ trunk/profile/profout.c
+@@ -1104,7 +1104,7 @@ make_output_icc(
+ if (iccver < icmVersion2_4) {
+ iccver = icmVersion2_4; /* Need 2.4.0 for Display intents */
+ if (verb)
+- fprintf(verbo,"Bumped ICC version to 2.4.0 to accomodate multiple Display intents\n");
++ fprintf(verbo,"Bumped ICC version to 2.4.0 to accommodate multiple Display intents\n");
+ }
+ }
+ if (wr_icco->set_version(wr_icco, iccver) != 0)
+Index: trunk/render/thscreen.c
+===================================================================
+--- trunk.orig/render/thscreen.c
++++ trunk/render/thscreen.c
+@@ -636,7 +636,7 @@ thscreen *new_thscreen(
+ mrang = 65535.0/(t->oelev - 1.0);
+ DBG(("new_thscreen() raw modulation rande = %f\n",mrang));
+
+- /* Modify the modulation range to accomodate any level overlap */
++ /* Modify the modulation range to accommodate any level overlap */
+ if (olap > 0.0 && t->oelev > 2) {
+ mrang = ((t->oelev - 2.0) * olap * mrang + 65535.0)/(t->oelev - 1.0);
+ DBG(("new_thscreen() modulation adjusted for overlap = %f\n",mrang));
+Index: trunk/xicc/xspect.c
+===================================================================
+--- trunk.orig/xicc/xspect.c
++++ trunk/xicc/xspect.c
+@@ -4454,7 +4454,7 @@ void xspect_plot10p(xspect *sp[10], int
+ /* Given an emission spectrum, set the UV output to the given level. */
+ /* The shape of the UV is taken from FWA1_stim, and the level is */
+ /* with respect to the Y of the input spectrum. */
+-/* The output range is extended to accomodate the UV wavelengths */
++/* The output range is extended to accommodate the UV wavelengths */
+ void xsp_setUV(xspect *out, xspect *in, double uvlevel) {
+ int i, xs, xe;
+ double ww, avg;
+Index: trunk/spectro/ccxxmake.c
+===================================================================
+--- trunk.orig/spectro/ccxxmake.c
++++ trunk/spectro/ccxxmake.c
+@@ -399,7 +399,7 @@ int main(int argc, char *argv[]) {
+ /* COM port */
+ } else if (argv[fa][1] == 'c') {
+ fa = nfa;
+- if (na == NULL) usage(0,"Paramater expected following -c");
++ if (na == NULL) usage(0,"Parameter expected following -c");
+ comno = atoi(na);
+ if (comno < 1 || comno > 40) usage(0,"-c parameter %d out of range",comno);
+
+@@ -508,7 +508,7 @@ int main(int argc, char *argv[]) {
+ /* Serial port flow control */
+ } else if (argv[fa][1] == 'W') {
+ fa = nfa;
+- if (na == NULL) usage(0,"Paramater expected following -W");
++ if (na == NULL) usage(0,"Parameter expected following -W");
+ if (na[0] == 'n' || na[0] == 'N')
+ fc = fc_None;
+ else if (na[0] == 'h' || na[0] == 'H')
+@@ -597,7 +597,7 @@ int main(int argc, char *argv[]) {
+ strcat(outname, doccss ? ".ccss" : ".ccmx");
+
+ if (fakeseq && doccss)
+- error("Fake CCSS test not implemeted");
++ error("Fake CCSS test not implemented");
+
+ printf("\n");
+
+@@ -813,7 +813,7 @@ int main(int argc, char *argv[]) {
+ refs[i][2] = cols[i][2];
+ }
+ gotref = 1;
+- warning("Got two colorimetric files - assuming '%s' is the refrence",innames[0]);
++ warning("Got two colorimetric files - assuming '%s' is the reference",innames[0]);
+ refrmode = -1;
+ cbid = 0;
+
+Index: trunk/spectro/dispread.c
+===================================================================
+--- trunk.orig/spectro/dispread.c
++++ trunk/spectro/dispread.c
+@@ -414,7 +414,7 @@ int main(int argc, char *argv[]) {
+ /* COM port */
+ } else if (argv[fa][1] == 'c') {
+ fa = nfa;
+- if (na == NULL) usage(0,"Paramater expected following -c");
++ if (na == NULL) usage(0,"Parameter expected following -c");
+ comport = atoi(na);
+ if (comport < 1 || comport > 50) usage(0,"-c parameter %d out of range",comport);
+
+Index: trunk/spectro/fakeread.c
+===================================================================
+--- trunk.orig/spectro/fakeread.c
++++ trunk/spectro/fakeread.c
+@@ -814,7 +814,7 @@ int main(int argc, char *argv[])
+
+ /* We're assuming that the input space has a perfect black point... */
+
+- /* Lookup the ouput black point in XYZ PCS. We're assuming monotonicity.. */
++ /* Lookup the output black point in XYZ PCS. We're assuming monotonicity.. */
+ bp[0] = bp[1] = bp[2] = 0.0;
+ oluo->lookup(oluo, bp, bp);
+
+@@ -827,7 +827,7 @@ int main(int argc, char *argv[])
+ bt1886 == 1 ? egamma : tgamma, bt1886 == 1 ? 1 : 0);
+
+ if (verb)
+- printf("Gamma Curve: Using ouput black offset proportion %f\n",outoprop);
++ printf("Gamma Curve: Using output black offset proportion %f\n",outoprop);
+
+ if (bt1886 == 1) { /* Using effective gamma */
+ if (verb)
+@@ -980,7 +980,7 @@ int main(int argc, char *argv[])
+ else if (nmask == ICX_K && sep_ins == icSigCmykData)
+ gfudge = 2;
+ else if (icx_colorant_comb_match_icc(nmask, sep_ins) == 0) {
+- error("Separation ICC device space '%s' dosen't match TI1 '%s'",
++ error("Separation ICC device space '%s' doesn't match TI1 '%s'",
+ icm2str(icmColorSpaceSignature, sep_ins),
+ ident); /* Should free(). */
+ }
+@@ -989,7 +989,7 @@ int main(int argc, char *argv[])
+ if (icc_luo != NULL) {
+ /* Check if icc is compatible with .ti1 */
+ if (sep_outs != ins)
+- error("ICC device space '%s' dosen't match Separation ICC '%s'",
++ error("ICC device space '%s' doesn't match Separation ICC '%s'",
+ icm2str(icmColorSpaceSignature, ins),
+ icm2str(icmColorSpaceSignature, sep_outs));
+ } else if (mlu != NULL) {
+@@ -1014,12 +1014,12 @@ int main(int argc, char *argv[])
+ else {
+ if (!revlookup) {
+ if (icx_colorant_comb_match_icc(nmask, ins) == 0)
+- error("ICC device space '%s' dosen't match TI1 '%s'",
++ error("ICC device space '%s' doesn't match TI1 '%s'",
+ icm2str(icmColorSpaceSignature, ins),
+ ident); // Should free().
+ } else {
+ if (icx_colorant_comb_match_icc(nmask, outs) == 0)
+- error("ICC device space '%s' dosen't match TI1 '%s'",
++ error("ICC device space '%s' doesn't match TI1 '%s'",
+ icm2str(icmColorSpaceSignature, ins),
+ ident); // Should free().
+
+Index: trunk/profile/invprofcheck.c
+===================================================================
+--- trunk.orig/profile/invprofcheck.c
++++ trunk/profile/invprofcheck.c
+@@ -98,7 +98,7 @@ void usage(void) {
+ fprintf(stderr," -k Show CIEDE2000 delta E values\n");
+ fprintf(stderr," -w create %s visualisation (profile%s)\n",vrml_format(),vrml_ext());
+ fprintf(stderr," -x Use %s axes\n",vrml_format());
+- fprintf(stderr," -e Color vectors acording to delta E\n");
++ fprintf(stderr," -e Color vectors according to delta E\n");
+ fprintf(stderr," profile.icm Profile to check\n");
+ exit(1);
+ }
+Index: trunk/profile/profcheck.c
+===================================================================
+--- trunk.orig/profile/profcheck.c
++++ trunk/profile/profcheck.c
+@@ -59,7 +59,7 @@ usage(void) {
+ fprintf(stderr," -w create %s visualisation (iccprofile%s)\n",vrml_format(),vrml_ext());
+ fprintf(stderr," -x Use %s axes\n",vrml_format());
+ fprintf(stderr," -m Make %s lines a minimum of 0.5\n",vrml_format());
+- fprintf(stderr," -e Color vectors acording to delta E\n");
++ fprintf(stderr," -e Color vectors according to delta E\n");
+ fprintf(stderr," -h Plot a histogram of delta E's\n");
+ fprintf(stderr," -s Sort output by delta E\n");
+ fprintf(stderr," -P N.NN Create a pruned .ti3 with points less or equal to N.NN delta E\n");
+Index: trunk/spectro/ccwin.c
+===================================================================
+--- trunk.orig/spectro/ccwin.c
++++ trunk/spectro/ccwin.c
+@@ -830,7 +830,7 @@ int ddebug /* >0 to print debug sta
+ return NULL;
+ }
+
+- debugr2((errout,"new_ccwin: return sucessfully\n"));
++ debugr2((errout,"new_ccwin: return successfully\n"));
+
+ return p;
+ }
+Index: trunk/profile/colverify.c
+===================================================================
+--- trunk.orig/profile/colverify.c
++++ trunk/profile/colverify.c
+@@ -69,7 +69,7 @@ usage(void) {
+ fprintf(stderr," -D Use D50 100.0 as L*a*b* white reference\n");
+ fprintf(stderr," -c Show CIE94 delta E values\n");
+ fprintf(stderr," -k Show CIEDE2000 delta E values\n");
+- fprintf(stderr," -h [hist.txt] Plot a histogram of delta E's [Optionaly save points to .txt]\n");
++ fprintf(stderr," -h [hist.txt] Plot a histogram of delta E's [Optionally save points to .txt]\n");
+ fprintf(stderr," -s Sort patch values by error\n");
+ fprintf(stderr," -w create PCS %s vector visualisation (measured%s)\n",vrml_format(),vrml_ext());
+ fprintf(stderr," -W create PCS %s marker visualisation (measured%s)\n",vrml_format(),vrml_ext());
+Index: trunk/spectro/ex1.c
+===================================================================
+--- trunk.orig/spectro/ex1.c
++++ trunk/spectro/ex1.c
+@@ -189,7 +189,7 @@ ex1_init_coms(inst *pp, baud_rate br, fl
+
+ p->gotcoms = 1;
+
+- a1logd(p->log, 2, "ex1_init_coms: init coms has suceeded\n");
++ a1logd(p->log, 2, "ex1_init_coms: init coms has succeeded\n");
+
+ return inst_ok;
+ }
+@@ -930,7 +930,7 @@ ex1_interp_native_error(ex1 *p, int ec)
+ case EX1_FLASH_MAP:
+ return "Flash map is incompatible with firmware";
+ case EX1_DEFERRED:
+- return "Operation/Response deffered";
++ return "Operation/Response deferred";
+ default:
+ return NULL;
+ }
+@@ -1280,7 +1280,7 @@ static int ex1_save_calibration(ex1 *p)
+ calf_wrspec(&x, p->sconf.idark[0]);
+ calf_wrspec(&x, p->sconf.idark[1]);
+
+- a1logd(p->log,3,"nbytes = %d, Checkum = 0x%x\n",x.nbytes,x.chsum);
++ a1logd(p->log,3,"nbytes = %d, Checksum = 0x%x\n",x.nbytes,x.chsum);
+ calf_wints(&x, (int *)(&x.chsum), 1);
+
+ if (calf_done(&x))
+@@ -1467,7 +1467,7 @@ static void dump_command(ex1 *p, ORD8 *b
+ if (flags & EX1_FLAG_NACK)
+ a1logd(p->log, 0, " Negative acknowldgement response\n");
+ if (flags & EX1_FLAG_EXPTN)
+- a1logd(p->log, 0, " Exception occured\n");
++ a1logd(p->log, 0, " Exception occurred\n");
+ if (flags & EX1_FLAG_PVDEP)
+ a1logd(p->log, 0, " Protocol version is deprecated request\n");
+
+@@ -1753,7 +1753,7 @@ int nd /* nz to disable debug message
+ }
+
+ if (p->log->debug >= 8) {
+- a1logd(p->log,1,"\nex1_command: RECIEVING:\n");
++ a1logd(p->log,1,"\nex1_command: RECEIVING:\n");
+ dump_command(p, buf, rwbytes, p->log->debug);
+ }
+
+Index: trunk/ccast/ccmes.c
+===================================================================
+--- trunk.orig/ccast/ccmes.c
++++ trunk/ccast/ccmes.c
+@@ -85,7 +85,7 @@ char *ccmessv_emes(ccmessv_err rv) {
+ return "ccmes: connection has been closed";
+ }
+
+- return "Uknown ccmessv error";
++ return "Unknown ccmessv error";
+ }
+
+ #if defined(LOWVERBTRACE) || defined(DEBUG)
+Index: trunk/ccast/ccpacket.c
+===================================================================
+--- trunk.orig/ccast/ccpacket.c
++++ trunk/ccast/ccpacket.c
+@@ -133,7 +133,7 @@ char *ccpacket_emes(ccpacket_err rv) {
+ return "Packet: failed to read message";
+ }
+
+- return "Uknown ccpacket error";
++ return "Unknown ccpacket error";
+ }
+
+ /* Establish an ccpacket connection - implementation */
+@@ -187,13 +187,13 @@ static ccpacket_err connect_ccpacket_imp
+ #endif
+ if ((rv = setsockopt(p->sock, SOL_SOCKET, SO_RCVTIMEO, (const char*)&tv,
+ sizeof(tv))) < 0) {
+- DBG((g_log,0,"setsockopt timout failed with %d, errno %d",rv,ERRNO))
++ DBG((g_log,0,"setsockopt timeout failed with %d, errno %d",rv,ERRNO))
+ return ccpacket_connect;
+ }
+ tv = 2000;
+ if ((rv = setsockopt(p->sock, SOL_SOCKET, SO_SNDTIMEO, (const char*)&tv,
+ sizeof(tv))) < 0) {
+- DBG((g_log,0,"setsockopt timout failed with %d, errno %d",rv,ERRNO))
++ DBG((g_log,0,"setsockopt timeout failed with %d, errno %d",rv,ERRNO))
+ return ccpacket_connect;
+ }
+ #else
+@@ -207,14 +207,14 @@ static ccpacket_err connect_ccpacket_imp
+ #endif
+ if ((rv = setsockopt(p->sock, SOL_SOCKET, SO_RCVTIMEO, (const char*)&tv,
+ sizeof(tv))) < 0) {
+- DBG((g_log,0,"setsockopt timout failed with %d, errno %d",rv,ERRNO))
++ DBG((g_log,0,"setsockopt timeout failed with %d, errno %d",rv,ERRNO))
+ return ccpacket_connect;
+ }
+ tv.tv_sec = 2;
+ tv.tv_usec = 0;
+ if ((rv = setsockopt(p->sock, SOL_SOCKET, SO_SNDTIMEO, (const char*)&tv,
+ sizeof(tv))) < 0) {
+- DBG((g_log,0,"setsockopt timout failed with %d, errno %d",rv,ERRNO))
++ DBG((g_log,0,"setsockopt timeout failed with %d, errno %d",rv,ERRNO))
+ return ccpacket_connect;
+ }
+ #endif
+@@ -224,7 +224,7 @@ static ccpacket_err connect_ccpacket_imp
+ ling.l_linger = 2; /* Two seconds */
+ if ((rv = setsockopt(p->sock, SOL_SOCKET, SO_LINGER, (const char*)&ling,
+ sizeof(ling))) < 0) {
+- DBG((g_log,0,"setsockopt timout failed with %d, errno %d",rv,ERRNO))
++ DBG((g_log,0,"setsockopt timeout failed with %d, errno %d",rv,ERRNO))
+ return ccpacket_connect;
+ }
+ #endif /* NEVER */
+Index: trunk/spectro/xdg_bds.c
+===================================================================
+--- trunk.orig/spectro/xdg_bds.c
++++ trunk/spectro/xdg_bds.c
+@@ -879,7 +879,7 @@ char *xdg_errstr(xdg_error er) {
+ case xdg_nohome:
+ return "There is no $HOME";
+ case xdg_noalluserprofile:
+- return "Theres no $ALLUSERSPROFILE is no $ALLUSERSPROFILE";
++ return "There's no $ALLUSERSPROFILE is no $ALLUSERSPROFILE";
+ case xdg_nopath:
+ return "There is no resulting path";
+ case xdg_mallformed:
+Index: trunk/icc/icc.c
+===================================================================
+--- trunk.orig/icc/icc.c
++++ trunk/icc/icc.c
+@@ -1481,7 +1481,7 @@ static const char *string_TagSignature(i
+ case icSigViewingCondDescTag:
+ return "Viewing Condition Description";
+ case icSigViewingConditionsTag:
+- return "Viewing Condition Paramaters";
++ return "Viewing Condition Parameters";
+
+ /* ArgyllCMS private tag: */
+ case icmSigAbsToRelTransSpace:
+@@ -5785,7 +5785,7 @@ int icmSetMultiLutTables(
+ return icp->errc = 1;
+ }
+ if (pp[tn]->ttype != p->ttype) {
+- sprintf(icp->err,"icmSetMultiLutTables Tables have different Tage Type");
++ sprintf(icp->err,"icmSetMultiLutTables Tables have different Tag Type");
+ return icp->errc = 1;
+ }
+
+Index: trunk/gamut/isecvol.c
+===================================================================
+--- trunk.orig/gamut/isecvol.c
++++ trunk/gamut/isecvol.c
+@@ -174,7 +174,7 @@ printf("~1 doing triangle %d from %s gam
+ inout[i] = 0;
+ }
+
+-printf("~1 verticies outside = %d\n",nout);
++printf("~1 vertices outside = %d\n",nout);
+
+ /* If none are in, skip this triangle */
+ if (nout == 3)
+Index: trunk/gamut/viewgam.c
+===================================================================
+--- trunk.orig/gamut/viewgam.c
++++ trunk/gamut/viewgam.c
+@@ -355,7 +355,7 @@ main(int argc, char *argv[]) {
+ error("Input file doesn't contain exactly two tables");
+
+ if ((nverts = pp->t[0].nsets) <= 0)
+- error("No verticies");
++ error("No vertices");
+ if ((ntris = pp->t[1].nsets) <= 0)
+ error("No triangles");
+
+Index: trunk/gamut/gamut.c
+===================================================================
+--- trunk.orig/gamut/gamut.c
++++ trunk/gamut/gamut.c
+@@ -2152,8 +2152,8 @@ static void check_triangulation(gamut *s
+ for (j = i+1; j < 3; j++) {
+ if (tp->v[i] == tp->v[j]) {
+ failed = 1;
+- printf("Validation failed - duplicate verticies:\n");
+- printf("Triangle %d, has verticies %d %d %d\n", tp->n, tp->v[0]->n, tp->v[1]->n, tp->v[2]->n);
++ printf("Validation failed - duplicate vertices:\n");
++ printf("Triangle %d, has vertices %d %d %d\n", tp->n, tp->v[0]->n, tp->v[1]->n, tp->v[2]->n);
+ fflush(stdout);
+ }
+ }
+@@ -2165,7 +2165,7 @@ static void check_triangulation(gamut *s
+ if (tp->e[i] == tp->e[j]) {
+ failed = 1;
+ printf("Validation failed - duplicate connectivity:\n");
+- printf("Triangle %d, has verticies %d %d %d\n", tp->n, tp->v[0]->n, tp->v[1]->n, tp->v[2]->n);
++ printf("Triangle %d, has vertices %d %d %d\n", tp->n, tp->v[0]->n, tp->v[1]->n, tp->v[2]->n);
+ printf("Triangle %d, has edges %d %d %d\n", tp->n, tp->e[0]->n, tp->e[1]->n, tp->e[2]->n);
+ fflush(stdout);
+ }
+@@ -2187,7 +2187,7 @@ static void check_triangulation(gamut *s
+ /* for this triangle is correct */
+ if (ei1 != i) {
+ failed = 1;
+- printf("Validation failed - triangle edge index doesn't match record withing edge:\n");
++ printf("Validation failed - triangle edge index doesn't match record within edge:\n");
+ printf("Triangle %d, edge index %d edge %d has record %d\n", tp->n, i, e->n, ei1);
+ fflush(stdout);
+ }
+@@ -2206,9 +2206,9 @@ static void check_triangulation(gamut *s
+ if ((e->v[0] != tp->v[i] || e->v[1] != tp->v[(i+1) % 3])
+ && (e->v[1] != tp->v[i] || e->v[0] != tp->v[(i+1) % 3])) {
+ failed = 1;
+- printf("Validation failed - edge doesn't have same verticies as triangle expects:\n");
+- printf("Triangle %d, has verticies %d %d\n", tp->n, tp->v[i]->n, tp->v[(i+1) % 3]->n);
+- printf("Edge %d, has verticies %d %d\n", e->n, e->v[0]->n, e->v[1]->n);
++ printf("Validation failed - edge doesn't have same vertices as triangle expects:\n");
++ printf("Triangle %d, has vertices %d %d\n", tp->n, tp->v[i]->n, tp->v[(i+1) % 3]->n);
++ printf("Edge %d, has vertices %d %d\n", e->n, e->v[0]->n, e->v[1]->n);
+ fflush(stdout);
+ }
+
+@@ -2690,7 +2690,7 @@ gamut *s
+ ff[1] = fsz * foffs[i][0] + s->cent[1];
+ ff[2] = fsz * foffs[i][1] + s->cent[2];
+ if ((tvs[j++] = expand_gamut(s, ff)) == NULL) {
+- fprintf(stderr,"gamut: internal error - failed to register a fake initial verticies!\n");
++ fprintf(stderr,"gamut: internal error - failed to register a fake initial vertices!\n");
+ exit (-1);
+ }
+ }
+@@ -2699,7 +2699,7 @@ gamut *s
+ s->doingfake = 0;
+
+ #ifdef NEVER
+- printf("Initial verticies:\n");
++ printf("Initial vertices:\n");
+ for (i = 0; i < 4; i++) {
+ printf(" %d: %f %f %f\n",tvs[i]->n, tvs[i]->p[0], tvs[i]->p[1], tvs[i]->p[2]);
+ }
+@@ -6427,7 +6427,7 @@ char *filename
+
+
+ if ((nverts = gam->t[0].nsets) <= 0) {
+- fprintf(stderr,"No verticies");
++ fprintf(stderr,"No vertices");
+ return 1;
+ }
+ if ((ntris = gam->t[1].nsets) <= 0) {
+Index: trunk/rspl/rev.c
+===================================================================
+--- trunk.orig/rspl/rev.c
++++ trunk/rspl/rev.c
+@@ -5872,7 +5872,7 @@ int sdi /* Sub-simplex dimensionali
+ x->face = 0;
+
+ #ifdef DEBUG
+- printf("Verticies = ");
++ printf("vertices = ");
+ for (i = 0; i <= sdi; i++)
+ printf("%d ",vcmb[i]);
+ printf("\n");
+@@ -10344,7 +10344,7 @@ rspl *s
+ vc.nilist = 0;
+ xlist = NULL;
+
+- DBG(("deleting verticies in all bxcells\n"));
++ DBG(("deleting vertices in all bxcells\n"));
+
+ /* The thinning may have deleted verticies from bxcell's that */
+ /* were not involved in the thinning, so go though all bxcells */
+@@ -10484,7 +10484,7 @@ rspl *s
+ //printf("~1 deleting vtx %d\n",vx->ix);
+ del_vtxrec_hash(&vc, vx->ix);
+ if (get_vtxrec(&vc, vx->ix) != NULL)
+- error("get_vtxrec suceeded after del_vtxrec_hash!");
++ error("get_vtxrec succeeded after del_vtxrec_hash!");
+ }
+ #else /* !DELETE_SHAD */
+ /* Keep track of deleted verticies that are in this bx, */
+@@ -11165,7 +11165,7 @@ rspl *s
+ }
+ }
+ printf("%d crossed triangles tested\n",notverts);
+- printf("%d hidden verticies retained for crossed triangles\n",nopreserved);
++ printf("%d hidden vertices retained for crossed triangles\n",nopreserved);
+ printf("Took %f secs to preserving crossing triangless\n",0.001 * (msec_time()-lmsec));
+ #endif
+ } /* End of preserve shadowed triangles */
+Index: trunk/spectro/munki.c
+===================================================================
+--- trunk.orig/spectro/munki.c
++++ trunk/spectro/munki.c
+@@ -114,7 +114,7 @@ munki_init_coms(inst *pp, baud_rate br,
+ return munki_interp_code(p, icoms2munki_err(se));
+ }
+
+- a1logd(p->log, 2, "munki_init_coms: init coms has suceeded\n");
++ a1logd(p->log, 2, "munki_init_coms: init coms has succeeded\n");
+
+ p->gotcoms = 1;
+ return inst_ok;
+@@ -526,7 +526,7 @@ munki_interp_error(inst *pp, munki_code
+ case MUNKI_INT_CREATE_EEPROM_STORE:
+ return "Error in creating EEProm store";
+ case MUNKI_INT_NEW_RSPL_FAILED:
+- return "Creating RSPL object faild";
++ return "Creating RSPL object failed";
+ case MUNKI_INT_CAL_SAVE:
+ return "Unable to save calibration to file";
+ case MUNKI_INT_CAL_RESTORE:
+Index: trunk/spectro/i1pro.c
+===================================================================
+--- trunk.orig/spectro/i1pro.c
++++ trunk/spectro/i1pro.c
+@@ -120,7 +120,7 @@ i1pro_init_coms(inst *pp, baud_rate br,
+ return i1pro_interp_code(p, icoms2i1pro_err(se));
+ }
+
+- a1logd(p->log, 2, "i1pro_init_coms: init coms has suceeded\n");
++ a1logd(p->log, 2, "i1pro_init_coms: init coms has succeeded\n");
+
+ p->gotcoms = 1;
+ return inst_ok;
+@@ -509,7 +509,7 @@ i1pro_interp_error(inst *pp, i1pro_code
+ case I1PRO_INT_EEPROM_DATA_MISSING:
+ return "EEProm data is missing";
+ case I1PRO_INT_NEW_RSPL_FAILED:
+- return "Creating RSPL object faild";
++ return "Creating RSPL object failed";
+ case I1PRO_INT_CAL_SAVE:
+ return "Unable to save calibration to file";
+ case I1PRO_INT_CAL_RESTORE:
+Index: trunk/xicc/mpp.c
+===================================================================
+--- trunk.orig/xicc/mpp.c
++++ trunk/xicc/mpp.c
+@@ -733,7 +733,7 @@ int use_fwa /* NZ to involke
+ error ("mpp->set_ilob, instrument doesn't have an FWA illuminent");
+
+ if (p->spc->set_fwa(p->spc, &inst, NULL, &white))
+- error ("mpp->set_ilob, set_fwa faild");
++ error ("mpp->set_ilob, set_fwa failed");
+ }
+
+ return 0;
+Index: trunk/plot/vrml.c
+===================================================================
+--- trunk.orig/plot/vrml.c
++++ trunk/plot/vrml.c
+@@ -646,7 +646,7 @@ double cc[3] /* Surface color, cc == NUL
+ fprintf(s->fp," ]\n");
+ fprintf(s->fp," }\n");
+ fprintf(s->fp,"\n");
+- fprintf(s->fp," coordIndex [ # Indexes of %s Verticies \n",
++ fprintf(s->fp," coordIndex [ # Indexes of %s Vertices \n",
+ lines ? "line" : "polygon");
+
+ /* Spit out the lines/triangles/quads */
+Index: trunk/target/ofps.c
+===================================================================
+--- trunk.orig/target/ofps.c
++++ trunk/target/ofps.c
+@@ -2612,7 +2612,7 @@ static int position_vtx(
+ if (tries > s->maxretries)
+ s->maxretries = tries;
+ #ifdef DEBUG
+- printf(" - comb %s suceeded on retry %d (max %d)\n",pcomb(di,vv->nix),tries,s->maxretries);
++ printf(" - comb %s succeeded on retry %d (max %d)\n",pcomb(di,vv->nix),tries,s->maxretries);
+ printf(" oog = %f, eperr = %f, ceperr = %f\n",vv->oog,vv->eperr,vv->ceperr);
+ #endif
+ //if (tries > 10)
+@@ -2627,7 +2627,7 @@ static int position_vtx(
+
+ #ifdef DUMP_FERR /* Create .tiff of dnsq function error */
+ if (tries >= DUMP_FERR) {
+- printf("Suceeded on retry %d, dumping debug rasters\n",tries);
++ printf("Succeeded on retry %d, dumping debug rasters\n",tries);
+
+ /* Re-run the last unsucessful dnsq, to trace the path */
+ pcx.debug = 1;
+@@ -6221,7 +6221,7 @@ static int ofps_findhit_vtxs(ofps *s, no
+ int hit = 0;
+
+ if (nn->ix < 0)
+- error("ofps_findhit_vtxs given gamut boudary node ix %d",nn->ix);
++ error("ofps_findhit_vtxs given gamut boundary node ix %d",nn->ix);
+
+ #ifdef DEBUG
+ if (s->agrid_init == 0)
+@@ -8023,7 +8023,7 @@ ofps *s
+ warning("Verify of incremental vertexes failed!");
+ printf("Verify of incremental vertexes failed!\n");
+ } else {
+- warning("Verify of incremental vertexes suceeded!");
++ warning("Verify of incremental vertexes succeeded!");
+ }
+ #ifdef DUMP_STRUCTURE
+ dump_node_vtxs(s, 1);
+@@ -8443,7 +8443,7 @@ int nopstop /* Debug - number of opti
+ fprintf(stderr,"Average dnsqs/position = %.2f\n",s->dnsqs/(double)s->positions);
+ fprintf(stderr,"Average function calls/dnsq = %.1f\n",s->funccount/(double)s->dnsqs);
+ fprintf(stderr,"Maximum function calls/dnsq = %d\n",s->maxfunc);
+- fprintf(stderr,"Average function calls/sucessful dnsq = %.2f\n",s->sucfunc/(double)s->sucdnsq);
++ fprintf(stderr,"Average function calls/successful dnsq = %.2f\n",s->sucfunc/(double)s->sucdnsq);
+ fprintf(stderr,"Average function calls/position = %.1f\n",s->funccount/(double)s->positions);
+ fprintf(stderr,"Maximum tries for dnsq sucess %d\n",s->maxretries);
+ fprintf(stderr,"Number of position_vtx failures %d\n",s->posfails);
+Index: trunk/spectro/synthread.c
+===================================================================
+--- trunk.orig/spectro/synthread.c
++++ trunk/spectro/synthread.c
+@@ -487,14 +487,14 @@ printf("~1 omax = %f %f %f\n", md.omax[0
+ else if (nmask == ICX_K && sep_ins == icSigCmykData)
+ gfudge = 2;
+ else if (icx_colorant_comb_match_icc(nmask, sep_ins) == 0) {
+- error("Separation ICC device space '%s' dosen't match TI1 '%s'",
++ error("Separation ICC device space '%s' doesn't match TI1 '%s'",
+ icm2str(icmColorSpaceSignature, sep_ins),
+ ident); /* Should free(). */
+ }
+
+ /* Check if separation ICC output is compatible with ICC/MPP/TI3 conversion */
+ if (sep_outs != ins)
+- error("Synthetic device space '%s' dosen't match Separation ICC '%s'",
++ error("Synthetic device space '%s' doesn't match Separation ICC '%s'",
+ icm2str(icmColorSpaceSignature, ins),
+ icm2str(icmColorSpaceSignature, sep_outs));
+ } else {
+@@ -504,7 +504,7 @@ printf("~1 omax = %f %f %f\n", md.omax[0
+ else if (nmask == ICX_K && ins == icSigCmykData)
+ gfudge = 2; /* Should allow for other colorant combo's that include black */
+ else if (icx_colorant_comb_match_icc(nmask, ins) == 0) {
+- error("Synthetic device space '%s' dosen't match TI1 '%s'",
++ error("Synthetic device space '%s' doesn't match TI1 '%s'",
+ icm2str(icmColorSpaceSignature, ins),
+ ident); // Should free().
+ }
+Index: trunk/spectro/smcube.c
+===================================================================
+--- trunk.orig/spectro/smcube.c
++++ trunk/spectro/smcube.c
+@@ -293,7 +293,7 @@ smcube_init_coms(inst *pp, baud_rate br,
+ }
+ amutex_unlock(p->lock);
+ }
+- a1logd(p->log, 2, "smcube_init_coms: init coms has suceeded\n");
++ a1logd(p->log, 2, "smcube_init_coms: init coms has succeeded\n");
+
+ p->gotcoms = 1;
+
+@@ -1248,7 +1248,7 @@ smcube_get_idle_time(smcube *p, int *pit
+ itime = read_ORD16_be(buf + 4);
+
+ if (!nd)
+- a1logd(p->log, 2, "smcube_get_idle_time: returing %d\n",itime);
++ a1logd(p->log, 2, "smcube_get_idle_time: returning %d\n",itime);
+
+ if (pitime != NULL)
+ *pitime = itime;
+@@ -1293,11 +1293,11 @@ smcube_fact_measure(smcube *p, double *X
+ XYZ[0] = IEEE754todouble(read_ORD32_be(buf + 4));
+ XYZ[1] = IEEE754todouble(read_ORD32_be(buf + 8));
+ XYZ[2] = IEEE754todouble(read_ORD32_be(buf + 12));
+- a1logd(p->log, 2, "smcube_fact_measure: returing L*a*b* %f %f %f\n",XYZ[0], XYZ[1], XYZ[2]);
++ a1logd(p->log, 2, "smcube_fact_measure: returning L*a*b* %f %f %f\n",XYZ[0], XYZ[1], XYZ[2]);
+
+ icmLab2XYZ(&icmD50_100, XYZ, XYZ);
+
+- a1logd(p->log, 2, "smcube_fact_measure: returing XYZ %f %f %f\n",XYZ[0], XYZ[1], XYZ[2]);
++ a1logd(p->log, 2, "smcube_fact_measure: returning XYZ %f %f %f\n",XYZ[0], XYZ[1], XYZ[2]);
+
+ return inst_ok;
+ }
+@@ -1339,7 +1339,7 @@ smcube_poll_measure(smcube *p, double to
+ p->XYZ[0] = IEEE754todouble(read_ORD32_be(buf + 4));
+ p->XYZ[1] = IEEE754todouble(read_ORD32_be(buf + 8));
+ p->XYZ[2] = IEEE754todouble(read_ORD32_be(buf + 12));
+- if (!nd) a1logd(p->log, 2, "smcube_poll_measure: returing L*a*b* %f %f %f\n",p->XYZ[0], p->XYZ[1], p->XYZ[2]);
++ if (!nd) a1logd(p->log, 2, "smcube_poll_measure: returning L*a*b* %f %f %f\n",p->XYZ[0], p->XYZ[1], p->XYZ[2]);
+
+ icmLab2XYZ(&icmD50_100, p->XYZ, p->XYZ);
+
+@@ -2066,7 +2066,7 @@ int static smcube_save_calibration(smcub
+ calf_wtime_ts(&x, &p->gdate, 1);
+ calf_wdoubles(&x, p->goff, 3);
+
+- a1logd(p->log,3,"nbytes = %d, Checkum = 0x%x\n",x.nbytes,x.chsum);
++ a1logd(p->log,3,"nbytes = %d, Checksum = 0x%x\n",x.nbytes,x.chsum);
+ calf_wints(&x, (int *)(&x.chsum), 1);
+
+ if (calf_done(&x))
+Index: trunk/spectro/kleink10.c
+===================================================================
+--- trunk.orig/spectro/kleink10.c
++++ trunk/spectro/kleink10.c
+@@ -729,7 +729,7 @@ int ix /* Klein calibration index 1
+
+ if (buf[0] != 'D' || buf[1] != '1') {
+ amutex_unlock(p->lock);
+- a1logd(p->log, 1, "k10_read_cal_matrix: didn't get echo'd commad D1\n");
++ a1logd(p->log, 1, "k10_read_cal_matrix: didn't get echo'd command D1\n");
+ return inst_protocol_error;
+ }
+
+@@ -2068,7 +2068,7 @@ int *pinstmsec) { /* Return instrument r
+ break;
+ }
+
+- a1logd(p->log, 2, "k10_meas_delay: stoped at sample %d time %f\n",i,samp[i].sec);
++ a1logd(p->log, 2, "k10_meas_delay: stopped at sample %d time %f\n",i,samp[i].sec);
+
+ /* Compute overall delay */
+ dispmsec = (int)(samp[i].sec * 1000.0 + 0.5);
+Index: trunk/spectro/i1d3.c
+===================================================================
+--- trunk.orig/spectro/i1d3.c
++++ trunk/spectro/i1d3.c
+@@ -2522,7 +2522,7 @@ i1d3_init_coms(inst *pp, baud_rate br, f
+ a1logd(p->log, 1, "i1d3_init_coms: failed with rv = 0x%x\n",ev);
+ return ev;
+ }
+- a1logd(p->log, 2, "i1d3_init_coms: suceeded\n");
++ a1logd(p->log, 2, "i1d3_init_coms: succeeded\n");
+
+ p->gotcoms = 1;
+ return inst_ok;
+@@ -3198,7 +3198,7 @@ int *pinstmsec) { /* Return instrumen
+ break;
+ }
+
+- a1logd(p->log, 2, "i1d3_meas_delay: stoped at sample %d time %f\n",i,samp[i].sec);
++ a1logd(p->log, 2, "i1d3_meas_delay: stopped at sample %d time %f\n",i,samp[i].sec);
+
+ /* Compute overall delay */
+ dispmsec = (int)(samp[i].sec * 1000.0 + 0.5);
+Index: trunk/spectro/i1disp.c
+===================================================================
+--- trunk.orig/spectro/i1disp.c
++++ trunk/spectro/i1disp.c
+@@ -1360,7 +1360,7 @@ i1disp_read_refrate(
+ *ref_rate = rrate;
+ return inst_ok;
+ } else {
+- a1logd(p->log, 3, "No discernable refresh frequency measured\n");
++ a1logd(p->log, 3, "No discernible refresh frequency measured\n");
+ if (ref_rate != NULL)
+ *ref_rate = 0.0;
+ return inst_misread;
+Index: trunk/ccast/ccmdns.c
+===================================================================
+--- trunk.orig/ccast/ccmdns.c
++++ trunk/ccast/ccmdns.c
+@@ -352,7 +352,7 @@ static int init_socket_mDNS(SOCKET *psoc
+ DWORD tv;
+ tv = 100;
+ if (setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (const char*)&tv, sizeof(tv)) < 0) {
+- DBG((g_log,0,"setsockopt timout failed with %d\n",ERRNO))
++ DBG((g_log,0,"setsockopt timeout failed with %d\n",ERRNO))
+ closesocket(sock);
+ return 1;
+ }
+@@ -363,7 +363,7 @@ static int init_socket_mDNS(SOCKET *psoc
+ tv.tv_sec = 0;
+ tv.tv_usec = 100 * 1000;
+ if (setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (const char*)&tv, sizeof(tv)) < 0) {
+- DBG((g_log,0,"setsockopt timout failed with %d\n",ERRNO))
++ DBG((g_log,0,"setsockopt timeout failed with %d\n",ERRNO))
+ closesocket(sock);
+ return 1;
+ }
+Index: trunk/spectro/spyd2.c
+===================================================================
+--- trunk.orig/spectro/spyd2.c
++++ trunk/spectro/spyd2.c
+@@ -2916,7 +2916,7 @@ spyd2_init_coms(inst *pp, baud_rate br,
+ return spyd2_interp_code((inst *)p, icoms2spyd2_err(se));
+ }
+
+- a1logd(p->log, 2, "spyd2_init_coms: suceeded\n");
++ a1logd(p->log, 2, "spyd2_init_coms: succeeded\n");
+
+ p->gotcoms = 1;
+ return inst_ok;
+@@ -3373,9 +3373,9 @@ spyd2_interp_error(inst *pp, int ec) {
+ case SPYD2_BADREADSIZE:
+ return "Didn't read expected amount of data";
+ case SPYD2_TRIGTIMEOUT:
+- return "Trigger timout";
++ return "Trigger timeout";
+ case SPYD2_OVERALLTIMEOUT:
+- return "Overall timout";
++ return "Overall timeout";
+ case SPYD2_BAD_EE_CRC:
+ return "Serial EEProm CRC failed";
+
+Index: trunk/spectro/specbos.c
+===================================================================
+--- trunk.orig/spectro/specbos.c
++++ trunk/spectro/specbos.c
+@@ -351,7 +351,7 @@ specbos_init_coms(inst *pp, baud_rate br
+ a1logd(p->log, 2, "specbos_init_coms: unrecognised model %04d\n",p->model);
+ return inst_unknown_model;
+ }
+- a1logd(p->log, 2, "specbos_init_coms: init coms has suceeded\n");
++ a1logd(p->log, 2, "specbos_init_coms: init coms has succeeded\n");
+
+ /* See if it's a 1501 or 1511 */
+ if (p->model == 1501) {
+@@ -1669,7 +1669,7 @@ char id[CALIDLEN] /* Condition identifi
+ if ((ev = specbos_get_n_a_cals((inst *)p, &needed, &available)) != inst_ok)
+ return ev;
+
+- a1logd(p->log,4,"specbos_calibrate: needed 0x%x, avaialble 0x%x\n",needed, available);
++ a1logd(p->log,4,"specbos_calibrate: needed 0x%x, available 0x%x\n",needed, available);
+
+ /* Translate inst_calt_all/needed into something specific */
+ if (*calt == inst_calt_all
+Index: trunk/plot/plot.c
+===================================================================
+--- trunk.orig/plot/plot.c
++++ trunk/plot/plot.c
+@@ -2459,7 +2459,7 @@ static void dprintf(char *fmt, ...) {
+ printf("~1 found NtQueryInformationProcess\n"); fflush(stdout);
+ if(NtQueryInformationProcess(GetCurrentProcess(), 0,
+ &pbi, sizeof(pbi), &ulSize) >= 0 && ulSize == sizeof(pbi)) {
+-printf("~1 NtQueryInformationProcess suceeded\n"); fflush(stdout);
++printf("~1 NtQueryInformationProcess succeeded\n"); fflush(stdout);
+
+ *(FARPROC *)&AttachConsole =
+ GetProcAddress(LoadLibraryA("kernel32.dll"), "AttachConsole");
+Index: trunk/spectro/dtp22.c
+===================================================================
+--- trunk.orig/spectro/dtp22.c
++++ trunk/spectro/dtp22.c
+@@ -276,7 +276,7 @@ dtp22_init_coms(inst *pp, baud_rate br,
+ return inst_coms_fail;
+ }
+
+- a1logd(p->log, 2, "dtp22_init_coms: init coms has suceeded\n");
++ a1logd(p->log, 2, "dtp22_init_coms: init coms has succeeded\n");
+
+ p->gotcoms = 1;
+ return inst_ok;
+Index: trunk/spectro/dtp41.c
+===================================================================
+--- trunk.orig/spectro/dtp41.c
++++ trunk/spectro/dtp41.c
+@@ -256,7 +256,7 @@ dtp41_init_coms(inst *pp, baud_rate br,
+ return inst_coms_fail;
+ }
+
+- a1logd(p->log, 2, "dtp41_init_coms: init coms has suceeded\n");
++ a1logd(p->log, 2, "dtp41_init_coms: init coms has succeeded\n");
+
+ p->gotcoms = 1;
+ return inst_ok;
+Index: trunk/xicc/xutils.c
+===================================================================
+--- trunk.orig/xicc/xutils.c
++++ trunk/xicc/xutils.c
+@@ -147,7 +147,7 @@ icc *read_embedded_icc(char *file_name)
+ TIFFSetWarningHandler(oldwarnh);
+ TIFFSetErrorHandlerExt(olderrhx);
+ TIFFSetWarningHandlerExt(oldwarnhx);
+- debug("TIFFOpen suceeded\n");
++ debug("TIFFOpen succeeded\n");
+
+ if (TIFFGetField(rh, TIFFTAG_ICCPROFILE, &size, &tag) == 0 || size == 0) {
+ debug2((errout,"no ICC profile found in '%s'\n",file_name));
+Index: trunk/spectro/rspec.c
+===================================================================
+--- trunk.orig/spectro/rspec.c
++++ trunk/spectro/rspec.c
+@@ -957,7 +957,7 @@ int calf_open(calf *x, a1log *log, char
+ }
+ xdg_free(cal_paths, no_paths);
+
+- a1logd(x->log,2,"calf_open: suceeded\n");
++ a1logd(x->log,2,"calf_open: succeeded\n");
+
+ return 0;
+ }
+Index: trunk/spectro/chartread.c
+===================================================================
+--- trunk.orig/spectro/chartread.c
++++ trunk/spectro/chartread.c
+@@ -1171,7 +1171,7 @@ a1log *log /* verb, debug & error log
+
+ /* Or something is wrong with instrument capabilities */
+ } else {
+- printf("\nNo reasonable trigger mode avilable for this instrument\n");
++ printf("\nNo reasonable trigger mode available for this instrument\n");
+ it->del(it);
+ return -1;
+ }
+@@ -1684,7 +1684,7 @@ a1log *log /* verb, debug & error log
+
+ /* Or something is wrong with instrument capabilities */
+ } else {
+- printf("\nNo reasonable trigger mode avilable for this instrument\n");
++ printf("\nNo reasonable trigger mode available for this instrument\n");
+ it->del(it);
+ return -1;
+ }
+Index: trunk/spectro/illumread.c
+===================================================================
+--- trunk.orig/spectro/illumread.c
++++ trunk/spectro/illumread.c
+@@ -663,7 +663,7 @@ int main(int argc, char *argv[])
+
+ /* Or something is wrong with instrument capabilities */
+ } else {
+- printf("!!! No reasonable trigger mode avilable for this instrument !!!\n");
++ printf("!!! No reasonable trigger mode available for this instrument !!!\n");
+ continue;
+ }
+ if ((rv = it->get_set_opt(it, trigmode)) != inst_ok) {
+Index: trunk/profile/mppprof.c
+===================================================================
+--- trunk.orig/profile/mppprof.c
++++ trunk/profile/mppprof.c
+@@ -582,7 +582,7 @@ make_output_mpp(
+
+ /* Estimate the ink mixing model */
+ if (omix) {
+- printf("The ink mixing model isn't implimented here yet\n");
++ printf("The ink mixing model isn't implemented here yet\n");
+ }
+
+ /* create and write the cgats profile */
+Index: trunk/spectro/i1pro_imp.h
+===================================================================
+--- trunk.orig/spectro/i1pro_imp.h
++++ trunk/spectro/i1pro_imp.h
+@@ -437,7 +437,7 @@ void del_i1proimp(i1pro *p);
+ #define I1PRO_INT_SAVE_SUBT_MODE 0x65 /* Can't save calibration if in subt mode */
+ #define I1PRO_INT_NO_CAL_TO_SAVE 0x66 /* No calibration data to save */
+ #define I1PRO_INT_EEPROM_DATA_MISSING 0x67 /* EEProm data is missing */
+-#define I1PRO_INT_NEW_RSPL_FAILED 0x68 /* Creating RSPL object faild */
++#define I1PRO_INT_NEW_RSPL_FAILED 0x68 /* Creating RSPL object failed */
+ #define I1PRO_INT_CAL_SAVE 0x69 /* Unable to save calibration to file */
+ #define I1PRO_INT_CAL_RESTORE 0x6A /* Unable to restore calibration from file */
+ #define I1PRO_INT_CAL_TOUCH 0x6B /* Unable to touch calibration file */
+Index: trunk/spectro/munki_imp.h
+===================================================================
+--- trunk.orig/spectro/munki_imp.h
++++ trunk/spectro/munki_imp.h
+@@ -386,7 +386,7 @@ void del_munkiimp(munki *p);
+ #define MUNKI_INT_CIECONVFAIL 0x61 /* Creating spectral to CIE converted failed */
+ #define MUNKI_INT_MALLOC 0x62 /* Error in mallocing memory */
+ #define MUNKI_INT_CREATE_EEPROM_STORE 0x63 /* Error in creating EEProm store */
+-#define MUNKI_INT_NEW_RSPL_FAILED 0x64 /* Creating RSPL object faild */
++#define MUNKI_INT_NEW_RSPL_FAILED 0x64 /* Creating RSPL object failed */
+ #define MUNKI_INT_CAL_SAVE 0x65 /* Unable to save calibration to file */
+ #define MUNKI_INT_CAL_RESTORE 0x66 /* Unable to restore calibration from file */
+ #define MUNKI_INT_CAL_TOUCH 0x67 /* Unable to touch calibration file */
diff --git a/debian/patches/0105_dispwin_segfault.patch b/debian/patches/0105_dispwin_segfault.patch
new file mode 100644
index 0000000..3ead582
--- /dev/null
+++ b/debian/patches/0105_dispwin_segfault.patch
@@ -0,0 +1,20 @@
+Description: Add check for NULL pointer
+Author: Jörg Frings-Fürst <debian@jff-webhsoting.net>
+Bug-Debian: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=700253
+Forwarded: http://www.freelists.org/post/argyllcms/dispwin-bad-command-line-option-makes-dispwin-segfault
+Reviewed-by:
+Last-Update: 2015-08-23
+---
+This patch header follows DEP-3: http://dep.debian.net/deps/dep3/
+Index: trunk/spectro/dispwin.c
+===================================================================
+--- trunk.orig/spectro/dispwin.c
++++ trunk/spectro/dispwin.c
+@@ -6277,6 +6277,7 @@ main(int argc, char *argv[]) {
+
+ /* Display number */
+ else if (argv[fa][1] == 'd') {
++ if(na == NULL) usage(0, "-d parameter missing");
+ if (strncmp(na,"web",3) == 0
+ || strncmp(na,"WEB",3) == 0) {
+ webdisp = 8080;
diff --git a/debian/patches/0110_usb-db_new.patch b/debian/patches/0110_usb-db_new.patch
new file mode 100644
index 0000000..f826509
--- /dev/null
+++ b/debian/patches/0110_usb-db_new.patch
@@ -0,0 +1,19 @@
+Description: Use hwdb builtin, instead of the obsolete usb-db in the udev rules.
+Author: Dmitrijs Ledkovs <dmitrij.ledkov@ubuntu.com>
+Bug-Ubuntu: https://bugs.launchpad.net/bugs/1200185
+Bug-Debian: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=762887
+Last-Update: 2014-09-26
+---
+This patch header follows DEP-3: http://dep.debian.net/deps/dep3/
+Index: trunk/usb/55-Argyll.rules
+===================================================================
+--- trunk.orig/usb/55-Argyll.rules 2014-09-25 11:10:12.000000000 +0200
++++ trunk/usb/55-Argyll.rules 2014-09-26 14:08:21.067295380 +0200
+@@ -85,6 +85,6 @@
+ ENV{COLOR_MEASUREMENT_DEVICE}=="*?", ENV{ACL_MANAGE}!="*?", MODE="660", GROUP="plugdev"
+
+ # Set ID_VENDOR and ID_MODEL acording to VID and PID
+-TEST=="/lib/udev/usb-db", IMPORT{program}="usb-db %p"
++IMPORT{builtin}="hwdb --subsystem=usb"
+
+ LABEL="argyll_rules_end"
diff --git a/debian/patches/0115_hurd_PATH_MAX.patch b/debian/patches/0115_hurd_PATH_MAX.patch
new file mode 100644
index 0000000..86f338d
--- /dev/null
+++ b/debian/patches/0115_hurd_PATH_MAX.patch
@@ -0,0 +1,81 @@
+Description: Add on hurdi386 missing PATH_MAX
+Author: Jörg Frings-Fürst <debian@jff-webhosting.net>
+Bug-Debian: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=762774
+Last-Update: 2014-09-25
+---
+This patch header follows DEP-3: http://dep.debian.net/deps/dep3/
+Index: trunk/numlib/numsup.c
+===================================================================
+--- trunk.orig/numlib/numsup.c
++++ trunk/numlib/numsup.c
+@@ -47,6 +47,10 @@
+
+ /* Globals */
+
++#ifndef PATH_MAX
++#define PATH_MAX 4096
++#endif
++
+ char *exe_path = "\000"; /* Directory executable resides in ('/' dir separator) */
+ //char *error_program = "Unknown"; /* Name to report as responsible for an error */
+
+Index: trunk/spectro/mongoose.c
+===================================================================
+--- trunk.orig/spectro/mongoose.c
++++ trunk/spectro/mongoose.c
+@@ -46,6 +46,10 @@
+ added to /usr/lib/firewalld/services
+ */
+
++#ifndef PATH_MAX
++#define PATH_MAX 4096
++#endif
++
+ #if defined(_WIN32)
+ #define _CRT_SECURE_NO_WARNINGS // Disable deprecation warning in VS2005
+ #else
+Index: trunk/spectro/usbio_lx.c
+===================================================================
+--- trunk.orig/spectro/usbio_lx.c
++++ trunk/spectro/usbio_lx.c
+@@ -34,6 +34,10 @@
+ #define poll_x poll
+ #endif
+
++#ifndef PATH_MAX
++#define PATH_MAX 4096
++#endif
++
+ /* USB descriptors are little endian */
+
+ /* Take a word sized return buffer, and convert it to an unsigned int */
+Index: trunk/spectro/usbio_nt.c
+===================================================================
+--- trunk.orig/spectro/usbio_nt.c
++++ trunk/spectro/usbio_nt.c
+@@ -31,6 +31,10 @@
+ #define LIBUSBW1_PATH_MAX 512
+ #define LIBUSBW1_DEFAULT_TIMEOUT 5000
+
++#ifndef PATH_MAX
++#define PATH_MAX 4096
++#endif
++
+ /* USB descriptors are little endian */
+
+ /* Take a word sized return buffer, and convert it to an unsigned int */
+Index: trunk/spectro/hidio.c
+===================================================================
+--- trunk.orig/spectro/hidio.c
++++ trunk/spectro/hidio.c
+@@ -101,6 +101,10 @@
+ #endif
+ #endif
+
++#ifndef PATH_MAX
++#define PATH_MAX 4096
++#endif
++
+ #if defined(NT)
+
+ /* Declartions to enable HID access without using the DDK */
diff --git a/debian/patches/0120_kfreebsd.patch b/debian/patches/0120_kfreebsd.patch
new file mode 100644
index 0000000..2b4b622
--- /dev/null
+++ b/debian/patches/0120_kfreebsd.patch
@@ -0,0 +1,55 @@
+From: Steven Chamberlain <steven@pyro.eu.org>
+Subject: use FreeBSD USB I/O code on GNU/kFreeBSD
+
+Use the FreeBSD USB I/O code not just on __FreeBSD__ itself,
+but on any system having __FreeBSD_kernel__ (such as GNU/kFreeBSD).
+
+--- a/spectro/usbio.c
++++ b/spectro/usbio.c
+@@ -94,7 +94,7 @@
+ # include "usbio_ox.c"
+ # endif
+ # if defined(UNIX_X11)
+-# if defined(__FreeBSD__)
++# if defined(__FreeBSD_kernel__)
+ # include "usbio_bsd.c"
+ # else
+ # include "usbio_lx.c"
+--- a/spectro/usbio_bsd.c
++++ b/spectro/usbio_bsd.c
+@@ -37,7 +37,7 @@
+ #include <fcntl.h>
+ #include <glob.h>
+ #include <sys/ioctl.h>
+-#if defined(__FreeBSD__)
++#if defined(__FreeBSD_kernel__)
+ # include <dev/usb/usb_ioctl.h> /* Not sure what's going on with FreeBSD... */
+ #else
+ # include <dev/usb/usb.h> /* The usual include for BSD */
+@@ -59,7 +59,7 @@
+ ) {
+ int i, j;
+ char *paths[] = {
+-#if defined(__FreeBSD__)
++#if defined(__FreeBSD_kernel__)
+ "/dev/usb/[0-9]*.*.0", /* FreeBSD >= 8 */
+ "/dev/ugen[0-9]*", /* FreeBSD < 8, but no .E */
+ #else
+@@ -94,7 +94,7 @@
+ /* For all the nodes found by the glob */
+ for (i = 0; i < g.gl_pathc; i++) {
+
+-#if defined(__FreeBSD__)
++#if defined(__FreeBSD_kernel__)
+ /* Skip anything with an end point number */
+ if (j == 1 && strchr(g.gl_pathv[i], '.') != NULL)
+ continue;
+@@ -141,7 +141,7 @@
+
+ /* Create the base device path */
+ dpath = g.gl_pathv[i];
+-#if defined(__FreeBSD__)
++#if defined(__FreeBSD_kernel__)
+ if (j == 0) { /* Remove .0 */
+ if ((cp = strrchr(dpath, '.')) != NULL
+ && cp[1] == '0' && cp[2] == '\000')
diff --git a/debian/patches/0125_gcc5.patch b/debian/patches/0125_gcc5.patch
new file mode 100644
index 0000000..677fb44
--- /dev/null
+++ b/debian/patches/0125_gcc5.patch
@@ -0,0 +1,22 @@
+Description: Fix FTBFS with GCC 5
+Author: James Cowgill <james410@cowgill.org.uk>
+Bug-Debian: https://bugs.debian.org/777779
+Forwarded: no
+---
+This patch header follows DEP-3: http://dep.debian.net/deps/dep3/
+Index: trunk/icc/icc.h
+===================================================================
+--- trunk.orig/icc/icc.h
++++ trunk/icc/icc.h
+@@ -131,7 +131,11 @@
+ #define CF64PREC "LL" /* Constant precision specifier */
+
+ #ifndef ATTRIBUTE_NORETURN
++#ifdef _MSC_VER
+ # define ATTRIBUTE_NORETURN __declspec(noreturn)
++#else
++# define ATTRIBUTE_NORETURN __attribute__((noreturn))
++#endif
+ #endif
+
+ #else /* !_MSC_VER */
diff --git a/debian/patches/0130_openssl.patch b/debian/patches/0130_openssl.patch
new file mode 100644
index 0000000..5e000ee
--- /dev/null
+++ b/debian/patches/0130_openssl.patch
@@ -0,0 +1,19 @@
+Description: Enable all ssl connections
+Author: Jörg Frings-Fürst
+Bug-Debian: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=871427
+Last-Update: 2017-08-26
+---
+This patch header follows DEP-3: http://dep.debian.net/deps/dep3/
+Index: trunk/ccast/ccpacket.c
+===================================================================
+--- trunk.orig/ccast/ccpacket.c
++++ trunk/ccast/ccpacket.c
+@@ -157,7 +157,7 @@ static ccpacket_err connect_ccpacket_imp
+ if ((p->ctx = ssl_ctx_new(SSL_SERVER_VERIFY_LATER, 1)) == NULL)
+ #else
+ // Want to use TLS_client_method(), but older OpenSSL doesn't have it...
+- if ((p->ctx = SSL_CTX_new(TLSv1_client_method())) == NULL)
++ if ((p->ctx = SSL_CTX_new(TLS_client_method())) == NULL)
+ #endif
+ {
+ DBG((g_log,0, "connect ssl_ctx_new failed\n"))
diff --git a/debian/patches/series b/debian/patches/series
new file mode 100644
index 0000000..47582dd
--- /dev/null
+++ b/debian/patches/series
@@ -0,0 +1,8 @@
+0105_dispwin_segfault.patch
+0100_spelling.patch
+0001_jam.patch
+0115_hurd_PATH_MAX.patch
+#0110_usb-db_new.patch
+#0120_kfreebsd.patch
+0125_gcc5.patch
+0130_openssl.patch
diff --git a/debian/repack.sh b/debian/repack.sh
new file mode 100755
index 0000000..4b986be
--- /dev/null
+++ b/debian/repack.sh
@@ -0,0 +1,56 @@
+#!/bin/sh -e
+# Repackaging script to be called by Uscan
+
+echo "Repackaging ..."
+TMP="../"
+PKG="`dpkg-parsechangelog|sed 's/^Source: //p;d'`_$2+repack.orig"
+
+
+echo "Extracting tarball ..."
+echo "$3 -->> $2"
+tar xzf "$3" -C "$TMP"
+cd "$TMP"
+
+echo "Rename source dir"
+ls -l
+mv Argyll_V$2 argyll_$2
+
+echo "Removing unwanted stuff ..."
+cd argyll_$2
+ls -l
+rm -f yajl/yajl_test.exe yajl/yajl_test.obj
+rm -fr usb/bin
+rm -fr jpeg
+rm -fr tiff
+rm -fr zlib
+rm -fr png
+chmod -R -x+X *
+ls -l
+#cd ..
+
+echo "Creating repack tarball ..."
+tar -caf "../argyll_$2+repack.orig.tar.xz" *
+
+cd ..
+echo "Clean tmp dir ..."
+rm -rf argyll_$2
+
+echo "Clean uscan files ..."
+rm -rf Argyll_V$2_src.zip
+#rm -rf argyll_$2.orig.tar.gz
+
+echo "Repackaged tarball created"
+
+
+
+ unzip ../Argyll_V${VERSION}_src.zip -d ../
+ mv ../Argyll_V${VERSION} ../argyll_${VERSION}
+ rm -f ../argyll_${VERSION}/yajl/yajl_test.exe ../argyll_${VERSION}/yajl/yajl_test.obj
+ rm -fr ../argyll_${VERSION}/usb/bin
+ rm -fr ../argyll_${VERSION}/jpeg
+ rm -fr ../argyll_${VERSION}/tiff
+ rm -fr ../argyll_${VERSION}/zlib
+ rm -fr ../argyll_${VERSION}/png
+ chmod -R -x+X ../argyll_${VERSION}/*
+ tar cJf ../argyll_${VERSION}+repack.orig.tar.xz ../argyll_${VERSION}
+ rm -fr ../argyll_${VERSION} ../Argyll_V${VERSION}_src.zip
diff --git a/debian/rules b/debian/rules
new file mode 100755
index 0000000..b2ea050
--- /dev/null
+++ b/debian/rules
@@ -0,0 +1,105 @@
+#!/usr/bin/make -f
+
+# Uncomment this to turn on verbose mode.
+#export DH_VERBOSE=1
+
+include /usr/share/dpkg/pkg-info.mk
+
+export DH_OPTIONS
+
+# hardening
+export DEB_BUILD_MAINT_OPTIONS = hardening=+all
+
+NO_PROC=$(shell cat /proc/cpuinfo | grep processor | wc -l)
+
+JAMCMDLINE = -q -fJambase -j$(NO_PROC) -sPREFIX=/usr -sDESTDIR=$(CURDIR)/debian/tmp -sREFSUBDIR=share/color/argyll/ref
+CRDATE=$(shell LC_ALL=C date --utc --date='@$(SOURCE_DATE_EPOCH)' "+%a %b %d %T %Y")
+
+%:
+ dh $@
+
+override_dh_auto_build:
+ find $(CURDIR) -name "*" | xargs chmod -R -x+X
+ chmod a+x $(CURDIR)/debian/rules
+ jam $(JAMCMDLINE) all
+
+override_dh_auto_install:
+ jam $(JAMCMDLINE) dirs
+ jam $(JAMCMDLINE) install
+ rm $(CURDIR)/debian/tmp/usr/bin/License.txt
+ #
+ # Make build results reproducible.
+ #
+ sed -i 's/CREATED.*/CREATED $(CRDATE)/' $(CURDIR)/debian/tmp/usr/share/color/argyll/ref/RefMediumGamut.gam
+ sed -i 's/CREATED.*/CREATED $(CRDATE)/' $(CURDIR)/debian/tmp/usr/share/color/argyll/ref/linear.cal
+ sed -i 's/CREATED.*/CREATED $(CRDATE)/' $(CURDIR)/debian/tmp/usr/share/color/argyll/ref/strange.cal
+
+override_dh_installdocs:
+ dh_installdocs
+ rm -f $(CURDIR)/debian/argyll-doc/usr/share/doc/argyll-doc/License.txt
+ rm -f $(CURDIR)/debian/argyll-doc/usr/share/doc/argyll-doc/License2.txt
+ rm -f $(CURDIR)/debian/argyll-doc/usr/share/doc/argyll-doc/License3.txt
+ rm -f $(CURDIR)/debian/argyll-doc/usr/share/doc/argyll-doc/License4.txt
+ rm -f $(CURDIR)/debian/argyll-doc/usr/share/doc/argyll-doc/DocLicense.txt
+ rm -f $(CURDIR)/debian/argyll-doc/usr/share/doc/argyll-doc/afiles
+
+override_dh_installchangelogs:
+ dh_installchangelogs log.txt
+
+override_dh_builddeb:
+ dh_builddeb
+
+override_dh_compress:
+ dh_compress -X.html
+
+override_dh_strip:
+ dh_strip --dbgsym-migration='argyll-dbg (<< 1.9.2+repack-2~)'
+
+build-manpages:
+ help2man -N --no-discard-stderr --name="Apply device calibration to an ICC profile." debian/tmp/usr/bin/applycal > debian/man/applycal.1
+ help2man -N --no-discard-stderr --name="Dump an ICC file in human readable form." debian/tmp/usr/bin/iccdump > debian/man/iccdump.1
+ help2man -N --no-discard-stderr --name="Translate colors through an ICC profile." debian/tmp/usr/bin/icclu > debian/man/icclu.1
+ help2man -N --no-discard-stderr --name="Average or merge values in .ti3 like files." debian/tmp/usr/bin/average > debian/man/average.1
+ help2man -N --no-discard-stderr --name="Convert Colorblind raw device profile data to Argyll data." debian/tmp/usr/bin/cb2ti3 > debian/man/cb2ti3.1
+ help2man -N --no-discard-stderr --name="Color Correct a TIFF file using any sequence of ICC profiles or Calibrations." debian/tmp/usr/bin/cctiff > debian/man/cctiff.1
+ help2man -N --no-discard-stderr --name="Create CCMX or CCSS." debian/tmp/usr/bin/ccxxmake > debian/man/ccxxmake.1
+ help2man -N --no-discard-stderr --name="Read Target Test Chart." debian/tmp/usr/bin/chartread > debian/man/chartread.1
+ help2man -N --no-discard-stderr --name="Link ICC profiles." debian/tmp/usr/bin/collink > debian/man/collink.1
+ help2man -N --no-discard-stderr --name="Create ICC profile." debian/tmp/usr/bin/colprof > debian/man/colprof.1
+ help2man -N --no-discard-stderr --name="Verify CIE values." debian/tmp/usr/bin/colverify > debian/man/colverify.1
+ help2man -N --no-discard-stderr --name="Calibrate a Display." debian/tmp/usr/bin/dispcal > debian/man/dispcal.1
+ help2man -N --no-discard-stderr --name="Read a Display." debian/tmp/usr/bin/dispread > debian/man/dispread.1
+ help2man -N --no-discard-stderr --name="Test display patch window, Set Video LUTs, Install profiles." debian/tmp/usr/bin/dispwin > debian/man/dispwin.1
+ help2man -N --no-discard-stderr --name="Extract an ICC profile from a TIFF file." debian/tmp/usr/bin/extracticc > debian/man/extracticc.1
+ help2man -N --no-discard-stderr --name="Extract a text tag from an ICC profile." debian/tmp/usr/bin/extractttag > debian/man/extractttag.1
+ help2man -N --no-discard-stderr --name="Create a fake CMY data file from a CMYK profile." debian/tmp/usr/bin/fakeCMY > debian/man/fakeCMY.1
+ help2man -N --no-discard-stderr --name="Fake test chart reader - lookup values in ICC/MPP profile." debian/tmp/usr/bin/fakeread > debian/man/fakeread.1
+ help2man -N --no-discard-stderr --name="Convert a TIFF file to monochrome using an ICC device profile." debian/tmp/usr/bin/greytiff > debian/man/greytiff.1
+ help2man -N --no-discard-stderr --name="Dump an ICC file in human readable form." debian/tmp/usr/bin/iccdump > debian/man/iccdump.1
+ help2man -N --no-discard-stderr --name="Create Lab/Jab gamut plot." debian/tmp/usr/bin/iccgamut > debian/man/iccgamut.1
+ help2man -N --no-discard-stderr --name="Measure an illuminant." debian/tmp/usr/bin/illumread > debian/man/illumread.1
+ help2man -N --no-discard-stderr --name="Check fwd to bwd relative transfer of an ICC file." debian/tmp/usr/bin/invprofcheck > debian/man/invprofcheck.1
+ help2man -N --no-discard-stderr --name="Convert Kodak raw printer profile data to Argyll print data." debian/tmp/usr/bin/kodak2ti3 > debian/man/kodak2ti3.1
+ help2man -N --no-discard-stderr --name="Check Model Printer Profile." debian/tmp/usr/bin/mppcheck > debian/man/mppcheck.1
+ help2man -N --no-discard-stderr --name="Translate colors through an MPP profile." debian/tmp/usr/bin/mpplu > debian/man/mpplu.1
+ help2man -N --no-discard-stderr --name="Create Model Printer Profile." debian/tmp/usr/bin/mppprof > debian/man/mppprof.1
+ help2man -N --no-discard-stderr --name="List information about the FILEs." debian/tmp/usr/bin/oeminst > debian/man/oeminst.1
+ help2man -N --no-discard-stderr --name="Create printer calibration." debian/tmp/usr/bin/printcal > debian/man/printcal.1
+ help2man -N --no-discard-stderr --name="Generate Target PostScrip file." debian/tmp/usr/bin/printtarg > debian/man/printtarg.1
+ help2man -N --no-discard-stderr --name="Check accuracy of ICC profile." debian/tmp/usr/bin/profcheck > debian/man/profcheck.1
+ help2man -N --no-discard-stderr --name="Create abstract correction profile given table of absolute CIE correction values." debian/tmp/usr/bin/refine > debian/man/refine.1
+ help2man -N --no-discard-stderr --name="Invert AtoB1 to make BtoA1 for CMYK profiles." debian/tmp/usr/bin/revfix > debian/man/revfix.1
+ help2man -N --no-discard-stderr --name="Scanin." debian/tmp/usr/bin/scanin > debian/man/scanin.1
+ help2man -N --no-discard-stderr --name="Convert spectral .ti3 file." debian/tmp/usr/bin/spec2cie > debian/man/spec2cie.1
+ help2man -N --no-discard-stderr --name="Plot spectrum and calculate CCT and VCT." debian/tmp/usr/bin/specplot > debian/man/specplot.1
+ help2man -N --no-discard-stderr --name="Split a .ti3 into two." debian/tmp/usr/bin/splitti3 > debian/man/splitti3.1
+ help2man -N --no-discard-stderr --name="Read Print Spot values." debian/tmp/usr/bin/spotread > debian/man/spotread.1
+ help2man -N --no-discard-stderr --name="Create a synthetic calibration file." debian/tmp/usr/bin/synthcal > debian/man/synthcal.1
+ help2man -N --no-discard-stderr --name="Synthetic device model test chart reader." debian/tmp/usr/bin/synthread > debian/man/synthread.1
+ help2man -N --no-discard-stderr --name="Generate Target deviceb test chart color values." debian/tmp/usr/bin/targen > debian/man/targen.1
+ help2man -N --no-discard-stderr --name="Create VRML image of the gamut surface of a TIFF." debian/tmp/usr/bin/tiffgamut > debian/man/tiffgamut.1
+ help2man -N --no-discard-stderr --name="Create test images, default hex RGB surface and wedge." debian/tmp/usr/bin/timage > debian/man/timage.1
+ help2man -N --no-discard-stderr --name="Convert Gretag/Logo or X-Rite ColorPport raw RGB or CMYK device profile data to Argyll CGATS data." debian/tmp/usr/bin/txt2ti3 > debian/man/txt2ti3.1
+ help2man -N --no-discard-stderr --name="View gamuts." debian/tmp/usr/bin/viewgam > debian/man/viewgam.1
+ help2man -N --no-discard-stderr --name="Translate colors through an xicc." debian/tmp/usr/bin/xicclu > debian/man/xicclu.1
+ help2man -N --no-discard-stderr --name="Convert LightSpace raw RGB device profile data to Argyll CGATS dat" debian/tmp/usr/bin/ls2ti3 > debian/man/ls2ti3.1
diff --git a/debian/source/format b/debian/source/format
new file mode 100644
index 0000000..163aaf8
--- /dev/null
+++ b/debian/source/format
@@ -0,0 +1 @@
+3.0 (quilt)
diff --git a/debian/tools/buildman.sh b/debian/tools/buildman.sh
new file mode 100644
index 0000000..fee4393
--- /dev/null
+++ b/debian/tools/buildman.sh
@@ -0,0 +1,13 @@
+#!/bin/bash
+
+cd ../../
+
+quilt push -a
+
+debian/rules override_dh_auto_build
+debian/rules override_dh_auto_install
+debian/rules build-manpages
+debian/rules override_dh_auto_clean
+
+quilt pop -a
+
diff --git a/debian/watch b/debian/watch
new file mode 100644
index 0000000..8592949
--- /dev/null
+++ b/debian/watch
@@ -0,0 +1,5 @@
+version=4
+
+#opts="dversionmangle=s/\+repack\d+$//" http://www.argyllcms.com/downloadsrc.html Argyll_V(.*)_src\.zip
+opts=dversionmangle=s/\+repack(.*)//,repacksuffix=+repack,compression=xz \
+https://www.argyllcms.com/downloadsrc.html Argyll_V(.*)_src\.zip