From f82fcb24f950e4b59b17eb6869b7705138228aa8 Mon Sep 17 00:00:00 2001 From: Leon van Kammen Date: Thu, 26 Feb 2026 22:40:23 +0100 Subject: [PATCH] mml bugfix + lovr-packager --- README.md | 3 + manyfold/cli/manyfold.sh | 14 +- .../1050-compile-textures.rb | 4 +- .../300-package_lovr_zip.sh | 4 +- .../experience_updated/300-package_mml.rb | 2 +- .../usr/src/app/app/components/model_card.rb | 6 - .../src/app/app/components/preview_frame.rb | 2 +- .../app/views/application/_footer.html.erb | 42 ++++++ .../app/views/layouts/application.html.erb | 16 ++ .../app/views/layouts/card_list_page.html.erb | 2 +- .../app/app/views/models/_janusroom.html.erb | 1 + .../src/app/app/views/models/show.html.erb | 38 ++++- manyfold/usr/src/app/public/about/index.html | 138 ++++++++++++++++-- .../usr/src/app/public/assets/xrforge.css | 9 ++ manyfold/usr/src/app/public/assets/xrforge.js | 15 ++ 15 files changed, 263 insertions(+), 33 deletions(-) create mode 100644 manyfold/usr/src/app/app/views/application/_footer.html.erb create mode 100644 manyfold/usr/src/app/public/assets/xrforge.js diff --git a/README.md b/README.md index 9ce2518..15a1d07 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,8 @@ # XRForge +XRForge is a pre-configured [Manyfold](https://manyfold.app) container (reproducably via [nix](https://nixos.org) dockertools). +It also contains some extra's, to better fit an XR audience & enable community libraries. + > Self-sovereign **XR Experiences** for teams & organisations, powered by FOSS ♥ diff --git a/manyfold/cli/manyfold.sh b/manyfold/cli/manyfold.sh index 8ab16e2..c66421a 100755 --- a/manyfold/cli/manyfold.sh +++ b/manyfold/cli/manyfold.sh @@ -9,13 +9,13 @@ test -n "$GODOT_VERSION" || export GODOT_VERSION=4.4.1-stable test -n "$IMPORT_INSTANCES" || export IMPORT_INSTANCES=1 test -n "$SERVER_CORS" || export SERVER_CORS=http://localhost:5577 test -n "$SERVER_JANUS" || export SERVER_JANUS=http://localhost:5566 -test -n "$HTTPS_ONLY" || unsafe=1 && export HTTPS_ONLY=disabled +test -n "$HTTPS_ONLY" || unsafe=1 test -n "$SECRET_KEY_BASE" || unsafe=1 && export SECRET_KEY_BASE=j1gf2cj3gfcjhf2j34298kjk2j3h4k test -n "$SUDO_RUN_UNSAFELY" || unsafe=1 && export SUDO_RUN_UNSAFELY=enabled test -n "$DATABASE_ADAPTER" || export DATABASE_ADAPTER=sqlite3 test -n "$MULTIUSER" || export MULTIUSER=enabled test -n "$FEDERATE_SERVERS" || export FEDERATE_SERVERS='"*"' -test -n "$PUBLIC_HOSTNAME" || export PUBLIC_HOSTNAME=localhost +test -n "$PUBLIC_HOSTNAME" #|| export PUBLIC_HOSTNAME=localhost test -n "$CADDY" && { export HTTPS_ONLY=enabled @@ -179,9 +179,11 @@ set_upload_path(){ } mount_dir(){ + mkdir /usr/src/app/public/lib find /mnt -type d -mindepth 1 -maxdepth 1 | grep -vE '(janusweb|view)' | while read dir; do echocolor "[$APPNAME]" "mounting $dir as library" add_lib_to_db "$dir" + ln -s "$dir" /usr/src/app/public/lib/. done } @@ -243,7 +245,8 @@ set_homepage(){ rename_app(){ echocolor "[$APPNAME]" "renaming manyfold to $APPNAME" sed -i 's/title: Manyfold/title: '$APPNAME'/g' /usr/src/app/config/locales/*.yml - sed -i 's|powered_by_html:.*|powered_by_html: Radically opensource-powered by Manyfold, XR Fragments, JanusWeb and NIX|g' /usr/src/app/config/locales/*.yml + sed -i 's|powered_by_html:.*|powered_by_html: \|\n Opensource-powered by Manyfold, XR Fragments, XR Hypermedia Federation, JanusWeb, NIX and NLnet|g' /usr/src/app/config/locales/*.yml + sed -i 's| by_html:.*| by_html: \|\n XRForge is a radically openource platform. A sysadmin can selfhost this platform right now!



This
opensource platform allows organisations to run on intranets or federate with others.|g' /usr/src/app/config/locales/*.yml echocolor "[$APPNAME]" "renaming 'model' to 'experience'" for dir in /usr/src/app/config/locales/*.yml /usr/src/app/config/locales/*/*.yml; do sed -i 's|Models|Experiences|g' "$dir" @@ -253,6 +256,9 @@ rename_app(){ done sed -i 's|File(s) uploaded succesfully|File(s) uploaded succesfully. The experience is now being (re)generated, please be patient and check back later|g' /usr/src/app/config/locales/models/en.yml sed -i 's|File(s) uploaded succesfully|File(s) uploaded succesfully. The experience is now being (re)generated, please be patient and check back later|g' /usr/src/app/config/locales/model_files/en.yml + + # link instead of duplicate models/collections immersive overviews + ln -s /usr/src/app/app/views/models/_janusroom.html.erb /usr/src/app/app/views/collections/. } federate_servers(){ @@ -387,6 +393,6 @@ usage(){ exit 0 } -test "$unsafe" = 1 && echocolor "[WARNING]" "default env-vars SECRET_KEY_BASE or SUDO_RUN_UNSAFELY are used. Please check: https://codeberg.org/coderofsalvation/xrforge/src/branch/master/manyfold" && echo +test "$unsafe" = 1 && echocolor "[WARNING]" "default env-vars SECRET_KEY_BASE or SUDO_RUN_UNSAFELY are used. Please check: https://codeberg.org/coderofsalvation/xrforge/src/branch/master/manyfold" 1>&2 && echo test -n "$1" && "$@" test -n "$1" || usage diff --git a/manyfold/root/hook.d/experience_updated/1050-compile-textures.rb b/manyfold/root/hook.d/experience_updated/1050-compile-textures.rb index 484da0d..4d46477 100755 --- a/manyfold/root/hook.d/experience_updated/1050-compile-textures.rb +++ b/manyfold/root/hook.d/experience_updated/1050-compile-textures.rb @@ -49,13 +49,14 @@ begin # Iterate through the images array gltf['images'].each_with_index do |img, i| - # move file to modeldirectory (but dont overwrite if user overwrite it) + # use matching texture in original modeldirectory (if generated via extract-textures shellscript) imgExts = ["jpg","png"] imgExts.each do |imgExt| new_filename = "#{dir}/#{filenameWithoutExt}_img#{i}.#{imgExt}" if File.exist?(new_filename) XRForge.log("🤔 detected #{File.basename(new_filename)}",logfile) XRForge.log("✅ importing #{File.basename(new_filename)} -> #{resource['path']}",logfile) + # here we update the glTF JSON with the matching texture img['uri'] = new_filename # NOTE: editing uri will cause assimp to drop image['name'] when exporting :/ update = true end @@ -64,6 +65,7 @@ begin if update File.write( gltf_path, JSON.pretty_generate(gltf) ) XRForge.log("✅ writing #{resource['path']}",logfile) + # convert scratch-model (with updated textures) to actual model system("assimp export #{gltf_path} #{resource['path']} --embed-textures") end end diff --git a/manyfold/root/hook.d/experience_updated/300-package_lovr_zip.sh b/manyfold/root/hook.d/experience_updated/300-package_lovr_zip.sh index ef6ab3c..672ce75 100755 --- a/manyfold/root/hook.d/experience_updated/300-package_lovr_zip.sh +++ b/manyfold/root/hook.d/experience_updated/300-package_lovr_zip.sh @@ -6,5 +6,7 @@ echo "[package_lovr_zip.sh] zipping lovr.zip" # overwrite empty lovr template project-zip with given URL test -n "$LOVR_TEMPLATE_ZIP" && timeout 50 wget "$LOVR_TEMPLATE_ZIP" -O ~/templates/template_lovr.zip +ln -s $(echo *.glb *.obj *.dae | awk '{print $1}') scene.glb cp ~/templates/template_lovr.zip .xrforge/lovr.zip -zip .xrforge/lovr.zip $(find . -maxdepth 1 -type f) +zip .xrforge/lovr.zip $(find -L . -maxdepth 1 -type f) +test -f scene.glb && rm scene.glb diff --git a/manyfold/root/hook.d/experience_updated/300-package_mml.rb b/manyfold/root/hook.d/experience_updated/300-package_mml.rb index c4bb36a..abf34b7 100755 --- a/manyfold/root/hook.d/experience_updated/300-package_mml.rb +++ b/manyfold/root/hook.d/experience_updated/300-package_mml.rb @@ -43,7 +43,7 @@ begin # https://viewer.mml.io/main/v1/?url=https%3A%2F%2Ffoo.org%2Fbar.mml mml = <<~MML - + MML File.write('.xrforge/scene.mml', mml) diff --git a/manyfold/usr/src/app/app/components/model_card.rb b/manyfold/usr/src/app/app/components/model_card.rb index c44257a..0082e4c 100644 --- a/manyfold/usr/src/app/app/components/model_card.rb +++ b/manyfold/usr/src/app/app/components/model_card.rb @@ -108,12 +108,6 @@ class Components::ModelCard < Components::Base div class: "col" do #open_button #whitespace - if ! @model.tags.where(name: "singleuser" ).any? - a alt: "start a meeting at this location", href: "/view/index.html?profile=default#janus.url="+model_url(@model), target: "_blank", class: "btn btn-secondary btn-sm" do - i class: "bi bi-telephone" - img src: "/assets/janusxr.svg", style: "width: 16px; margin-left: 7px; transform: translate(0px,-2px);" - end - end whitespace status_badges @model end diff --git a/manyfold/usr/src/app/app/components/preview_frame.rb b/manyfold/usr/src/app/app/components/preview_frame.rb index 026824d..c8a4810 100644 --- a/manyfold/usr/src/app/app/components/preview_frame.rb +++ b/manyfold/usr/src/app/app/components/preview_frame.rb @@ -16,7 +16,7 @@ class Components::PreviewFrame < Components::Base def view_template if @file - a href: "/view/index.html?profile=preview#janus.url="+model_url(@file.model), target: "_blank" do + a href: "/view/index.html?profile=default#janus.url="+model_url(@file.model), target: "_blank" do local end elsif @object.remote? diff --git a/manyfold/usr/src/app/app/views/application/_footer.html.erb b/manyfold/usr/src/app/app/views/application/_footer.html.erb new file mode 100644 index 0000000..39642ff --- /dev/null +++ b/manyfold/usr/src/app/app/views/application/_footer.html.erb @@ -0,0 +1,42 @@ + diff --git a/manyfold/usr/src/app/app/views/layouts/application.html.erb b/manyfold/usr/src/app/app/views/layouts/application.html.erb index 8b50bc2..98fdc84 100644 --- a/manyfold/usr/src/app/app/views/layouts/application.html.erb +++ b/manyfold/usr/src/app/app/views/layouts/application.html.erb @@ -40,5 +40,21 @@ <%= render "application/footer" %> + +
+ +
+ + + <%= "".html_safe %> + diff --git a/manyfold/usr/src/app/app/views/layouts/card_list_page.html.erb b/manyfold/usr/src/app/app/views/layouts/card_list_page.html.erb index 164819c..08ea1d6 100644 --- a/manyfold/usr/src/app/app/views/layouts/card_list_page.html.erb +++ b/manyfold/usr/src/app/app/views/layouts/card_list_page.html.erb @@ -3,7 +3,7 @@
- + <%= yield :items %>
diff --git a/manyfold/usr/src/app/app/views/models/_janusroom.html.erb b/manyfold/usr/src/app/app/views/models/_janusroom.html.erb index 1e4bb00..7eec6e0 100644 --- a/manyfold/usr/src/app/app/views/models/_janusroom.html.erb +++ b/manyfold/usr/src/app/app/views/models/_janusroom.html.erb @@ -19,6 +19,7 @@ + <% @models.each_with_index do |model, i| %> diff --git a/manyfold/usr/src/app/app/views/models/show.html.erb b/manyfold/usr/src/app/app/views/models/show.html.erb index 7c9ea1c..14754cc 100644 --- a/manyfold/usr/src/app/app/views/models/show.html.erb +++ b/manyfold/usr/src/app/app/views/models/show.html.erb @@ -60,12 +60,12 @@ end


- 🚀 (Re)generating the experience..

+ 🚀 (Re)generating the experience..
☕ Please be patient or check back later.
<% else %> - + <% end %>
<% else %> @@ -134,7 +134,7 @@ end <%== jml %> --> - <% if File.exist?( filePath+".xrforge/scene.gltf" ) %> + <% if File.exist?( filePath+".xrforge/scene.gltf" ) && ENV['FEDERATE_DRIVE_HOST'] %> ⁂ @@ -177,7 +177,7 @@ end <% end %> - <% if File.exist?( filePath+".xrforge/scene.mml" ) %> + <% if File.exist?( filePath+".xrforge/scene.mml" ) && ENV['FEDERATE_DRIVE_HOST'] %> ⁂ @@ -210,6 +210,8 @@ end <%= link_to @model.creator.name, @model.creator, itemprop: "author" %> <% end %> + + <% if ENV['FEDERATE_DRIVE_HOST'] %> @@ -232,6 +234,7 @@ end + <% end %> <% if @model.collection %> @@ -308,6 +311,7 @@ end <%= card :secondary, "Packages" do %> + <% if ENV['FEDERATE_DRIVE_HOST'] %>
@@ -331,6 +335,32 @@ end
+ <% end %> + <% if ENV['FEDERATE_DRIVE_HOST'] %> + + + + + +
+ + + <%= link_to "loVR project", ENV['FEDERATE_DRIVE_HOST']+"/"+@model.library.name+"/"+@model.path.gsub("#","%23")+"/.xrforge/lovr.zip" %> + +
+ +
+   + + This is a LoVR app (startingpoint) which wraps your (3D file) experience.
+ Godot is a Free and Opensource Game engine.
+ You can modify and run the app on these platforms: + WARNING: use progressive enhancement so your 3D file experience will always run in other XR Fragment viewers. +
+
+
+
+ <% end %> <% end %> <%= card :secondary, t("layouts.card_list_page.actions_heading") do %> diff --git a/manyfold/usr/src/app/public/about/index.html b/manyfold/usr/src/app/public/about/index.html index a5e54b8..a6651c1 100644 --- a/manyfold/usr/src/app/public/about/index.html +++ b/manyfold/usr/src/app/public/about/index.html @@ -12,6 +12,7 @@ + @@ -62,13 +63,13 @@

Turn files into AR/VR 🥽 experiences


- + Check the sourcecode
- irc.isvery.ninja port 443 via ObsidianIRC + docker run codeforge.org/coderofsalvation/xrforge:latest
    - + chat with community
irc.isvery.ninja port 443 via ObsidianIRC @@ -105,7 +106,7 @@

Why people want XRForge

- The metaverse-hype has shown: people like 3D but existing 2D ecosystems are king.
+ The metaverse has shown: people like 3D but certain existing 2D ecosystems are king.
They're just more cost-efficient to use.
Hence, XRForge promotes projecting these ecosystems as virtual hyperlinked worlds.
XRForge can seed itself via local or remote @@ -213,14 +214,132 @@
+
+

Comparison

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
XRForgeOther solutions
(Meta Horizon e.g.)
No data-collection or login requirement
users can host content themselvesHTTP/DAT/IPFS Open Protocolswe own your data
User operated content and viewerswe control both
portals to remote contentJanusXR or XR Fragments protocol
Platform does not control networkuser-operated viewers and contentstakeholders can block anything
Platform to Platform portalsJanusXR or XR Fragments protocol
2D web to 3D translatorsJanusWeb translators
Embed 3D scene in regular file (pdf e.g.)supported via JanusWeb
Portals from/to Internet/Intranet
User can make content privateXRForge permissions or HTTP password
Spatial anchors / URL addressibilityJanusWeb and XR Fragments protocol
Direct portals to 3D fileJanusweb and XR Fragments protocol
Detect 3D scene in webpage URLJanusweb and XR Fragments protocol
Export data + Credible exitserver-export runs on opensource janusweb viewer
Lightweight (runs on Raspberry PI e.g.)
XR worlds at restcompute required for nonvisited spaces
Integrates with Fediversevia ActivityPub and RSS/HTML translators
Integrate with (own) datacloudsscroll down below +
+ + Via rclone: Google Drive, Amazon S3, Microsoft OneDrive, Dropbox, Backblaze B2, SFTP, Google Cloud Storage (GCS), Microsoft Azure Blob Storage, MinIO, FTP, WebDAV, Wasabi, Cloudflare R2, Local Filesystem, MEGA, pCloud, Nextcloud, DigitalOcean Spaces, Hetzner Storage Box, Box, Google Photos, iCloud Drive, SMB/CIFS, Oracle Object Storage, Proton Drive, IDrive e2, Alibaba Cloud (Aliyun) OSS, Yandex Disk, Hetzner Object Storage, OVHcloud, ownCloud, Scaleway, Koofr, Put.io, Seafile, Mail.ru, 1Fichier, Akamai NetStorage, Internet Archive, Jottacloud, Storj, Tencent COS, Huawei OBS, Qiniu Kodo, OpenStack Swift, Arvan Cloud AOS, Bizfly Cloud, Ceph, China Mobile Ecloud, Citrix ShareFile, Cloudinary, Cubbit, Digi Storage, Dreamhost, Drime, Enterprise File Fabric, Exaba, Fastmail Files, FileLu Cloud, Filen, Files.com, Gofile, HDFS, HiDrive, HTTP, ImageKit, Internxt, IONOS Cloud, Leviia, Liara, Linkbox, Magalu, Memory, OpenDrive, Oracle Cloud Storage, Outscale, Petabox, PikPak, Pixeldrain, Premiumize.me, QingStor, Quatrix, Rabata, RackCorp, Rackspace Cloud Files, Rsync.net, Scaleway, Seafile, Seagate Lyve Cloud, SeaweedFS, Selectel, Servercore, Spectra Logic, Storj, SugarSync, Uloz.to, Zoho WorkDrive + +
+ ❌ +
+ +
+ +
+
+
+
+

Supporter of Open XR Hypermedia stacks

+
@@ -398,14 +517,5 @@ - - diff --git a/manyfold/usr/src/app/public/assets/xrforge.css b/manyfold/usr/src/app/public/assets/xrforge.css index da54d01..f0a9969 100644 --- a/manyfold/usr/src/app/public/assets/xrforge.css +++ b/manyfold/usr/src/app/public/assets/xrforge.css @@ -493,3 +493,12 @@ input[type="radio"][id="browser"]:checked ~ .openlearning { transform: scale(0.8); } } + +.form-control::-moz-placeholder { + color: #888; + opacity: 1; +} +.form-control::placeholder { + color: #888; + opacity: 1; +} diff --git a/manyfold/usr/src/app/public/assets/xrforge.js b/manyfold/usr/src/app/public/assets/xrforge.js new file mode 100644 index 0000000..49422e0 --- /dev/null +++ b/manyfold/usr/src/app/public/assets/xrforge.js @@ -0,0 +1,15 @@ +window.notify = function(opts){ + let { html,title, subtitle, domid, timeout } = opts + let el = document.querySelector( domid || '#notification') + if( title ) el.querySelector(".toast-header").innerHTML = `${title}${subtitle||''}` + if( html ) el.querySelector(".toast-body").innerHTML = html + el.style.display = 'block' + setTimeout( () => el.style.display = 'none', timeout || 5000 ) +}; + +// telescopic text: +// a JS cheat whicht allows persisting unfolds +// uncomment this if you really want this +([...document.querySelectorAll('u')]).map( (u) => { + u.addEventListener('click', e => e.target.className = 'show' ) +});