This commit is contained in:
Leon van Kammen 2025-11-10 15:05:01 +01:00
parent d725c9c73a
commit 0ace214070
7 changed files with 534 additions and 367 deletions

View file

@ -1,5 +0,0 @@
#!/bin/sh
dir="$(dirname $1)"
cd "$dir"
echo "[package_experience.sh] zipping experience.zip"
zip -D ".xrforge/experience.zip * | tee -a .xrforge/log.txt

View file

View file

@ -19,78 +19,90 @@ begin
# Read and parse the JSON file # Read and parse the JSON file
data = JSON.parse( File.read( "datapackage.json" ) ) data = JSON.parse( File.read( "datapackage.json" ) )
logfile = File.join( File.dirname(filename), ".xrforge/log.txt" ) if data['keywords'].empty? || data['keywords'].include?('janusxr')
XRForge.log("✅ starting build janusXR XR scene", logfile) logfile = File.join( File.dirname(filename), ".xrforge/log.txt" )
# Extract the desired field (assuming the field is named 'model_file') XRForge.log("✅ starting build janusXR XR scene", logfile)
thumb_file = data['image']
XRForge.log("✅ thumbnail sidecar-file '#{thumb_file}' detected", logfile) # Extract the desired field (assuming the field is named 'model_file')
thumb_file = data['image']
# Get the base name of the thumbnail file without its extension XRForge.log("✅ thumbnail sidecar-file '#{thumb_file}' detected", logfile)
base_name = File.basename(thumb_file, File.extname(thumb_file))
model_file = nil # Initialize model_file to nil # Get the base name of the thumbnail file without its extension
base_name = File.basename(thumb_file, File.extname(thumb_file))
# Loop over the list of extensions model_file = nil # Initialize model_file to nil
XRForge::MODEL_EXT.each do |ext|
# Construct the filename with the current extension
filename = "#{base_name}#{ext}"
# Check if the file exists # Loop over the list of extensions
if File.exist?(filename) XRForge::MODEL_EXT.each do |ext|
XRForge.log("✅ 3D file '#{filename}' detected", logfile) # Construct the filename with the current extension
model_file = "#{dir.gsub("/mnt/","")}/#{filename}" # Store the found filename filename = "#{base_name}#{ext}"
break # Stop the loop once a file is found
# Check if the file exists
if File.exist?(filename)
XRForge.log("✅ 3D file '#{filename}' detected", logfile)
model_file = "#{dir.gsub("/mnt/","")}/#{filename}" # Store the found filename
break # Stop the loop once a file is found
else
# Log a message for the file that was not found, but don't stop
XRForge.log("⚠️ 3D file '#{filename}' not detected", logfile)
end
end
# Check if a model file was found after the loop
if model_file
XRForge.log("✅ Final model file: '#{model_file}'", logfile)
else else
# Log a message for the file that was not found, but don't stop XRForge.log("❌ No suitable 3D file found for XR Fragments- / JanusXR-compatible experience", logfile)
XRForge.log("⚠️ 3D file '#{filename}' not detected", logfile) end
# Get the value of the environment variable FEDERATE_DRIVE_HOST
federate_drive_host = ENV['FEDERATE_DRIVE_HOST']
# Define the HTML content using a multi-line string (heredoc)
# Ruby's heredoc allows for variable interpolation (using #{})
jml = <<~JML
<FireBoxRoom>
<Assets>
<assetobject id="experience" src="#{federate_drive_host}/#{model_file.gsub("#","%23")}"/>
</Assets>
<Room>
<object pos="0 0 0" collision_id="experience" id="experience" />
</Room>
</FireBoxRoom>
JML
html = <<~HTML
<!DOCTYPE html>
<html>
<head>
<title>janusxr room</title>
</head>
<body>
<script src="https://web.janusvr.com/janusweb.js"></script>
<janus-viewer>
#{jml}
</janus-viewer>
</body>
</html>
HTML
File.write('.xrforge/janusxr.html', html)
File.write('.xrforge/scene.jml', jml)
XRForge.log("✅ generated scene.jml", logfile)
XRForge.log("✅ generated janusxr.html", logfile)
XRForge.log(" ", logfile)
# tag it!
if ! data['keywords'].include?('janusxr')
data['keywords'].push('janusxr')
File.write(file_path, JSON.pretty_generate(data) )
end end
end end
# Check if a model file was found after the loop
if model_file
XRForge.log("✅ Final model file: '#{model_file}'", logfile)
else
XRForge.log("❌ No suitable 3D file found for XR Fragments- / JanusXR-compatible experience", logfile)
end
# Get the value of the environment variable FEDERATE_DRIVE_HOST
federate_drive_host = ENV['FEDERATE_DRIVE_HOST']
# Define the HTML content using a multi-line string (heredoc)
# Ruby's heredoc allows for variable interpolation (using #{})
jml = <<~HTML
<!DOCTYPE html>
<html>
<head>
<title>janusxr room</title>
</head>
<body>
<script src="https://web.janusvr.com/janusweb.js"></script>
<janus-viewer>
<FireBoxRoom>
<Assets>
<assetobject id="experience" src="#{federate_drive_host}/#{model_file.gsub("#","%23")}"/>
</Assets>
<Room>
<object pos="0 0 0" collision_id="experience" id="experience" />
</Room>
</FireBoxRoom>
</janus-viewer>
</body>
</html>
HTML
# Write the content to the specified file
# File.write is the concise equivalent of 'echo "$jml" > filename'
File.write('.xrforge/janusxr.html', jml)
XRForge.log("✅ written janusxr.html", logfile)
XRForge.log(" ", logfile)
rescue Errno::ENOENT rescue Errno::ENOENT
puts "File #{filename} not found" puts "File #{filename} not found"
rescue JSON::ParserError rescue JSON::ParserError

View file

@ -0,0 +1,87 @@
#!/usr/bin/env ruby
require 'json'
require_relative './../../xrforge.rb'
# Check if a filename is provided
if ARGV.length != 1
puts "Usage: #{$0} <path/to/experience/somefile.xxx>"
exit 1
end
filename = ARGV[0]
begin
# Change the directory
dir = File.dirname(filename)
Dir.chdir( File.dirname(filename) )
# Read and parse the JSON file
data = JSON.parse( File.read( "datapackage.json" ) )
if data['keywords'].empty? || data['keywords'].include?('mml')
logfile = File.join( File.dirname(filename), ".xrforge/log.txt" )
XRForge.log("✅ starting build mml XR scene", logfile)
# Extract the desired field (assuming the field is named 'model_file')
thumb_file = data['image']
XRForge.log("✅ thumbnail sidecar-file '#{thumb_file}' detected", logfile)
# Get the base name of the thumbnail file without its extension
base_name = File.basename(thumb_file, File.extname(thumb_file))
model_file = nil # Initialize model_file to nil
# Loop over the list of extensions
XRForge::MODEL_EXT.each do |ext|
# Construct the filename with the current extension
filename = "#{base_name}#{ext}"
# Check if the file exists
if File.exist?(filename)
XRForge.log("✅ 3D file '#{filename}' detected", logfile)
model_file = "#{dir.gsub("/mnt/","")}/#{filename}" # Store the found filename
break # Stop the loop once a file is found
else
# Log a message for the file that was not found, but don't stop
XRForge.log("⚠️ 3D file '#{filename}' not detected", logfile)
end
end
# Check if a model file was found after the loop
if model_file
XRForge.log("✅ Final model file: '#{model_file}'", logfile)
else
XRForge.log("❌ No suitable 3D file found for XR Fragments- / MML-compatible experience", logfile)
end
# Get the value of the environment variable FEDERATE_DRIVE_HOST
federate_drive_host = ENV['FEDERATE_DRIVE_HOST']
# https://viewer.mml.io/main/v1/?url=https%3A%2F%2Ffoo.org%2Fbar.mml
mml = <<~MML
<m-model src="#{federate_drive_host}/#{model_file.gsub("#","%23")}" anim-loop="true" anim-enabled="true"></m-model>
MML
File.write('.xrforge/scene.mml', mml)
XRForge.log("✅ generated scene.mml", logfile)
XRForge.log(" ", logfile)
# tag it!
if ! data['keywords'].include?('mml')
data['keywords'].push('mml')
File.write(file_path, JSON.pretty_generate(data) )
end
end
rescue Errno::ENOENT
puts "File #{filename} not found"
rescue JSON::ParserError
puts "Error parsing JSON from #{filename}"
rescue => e
puts "An error occurred: #{e.message}"
end

View file

@ -0,0 +1,32 @@
Features can be toggled via tags:<br>
<br>
<table class="table">
<tr>
<td>
<a class="badge rounded-pill bg-secondary tag">menu</a>
</td>
<td>
This will generate a navigator-menu <b>into</b> your main 3D file.<br>
The links can be edited <%= link_to "here", edit_model_path(@model) %>
</td>
</tr>
<tr>
<td>
<a class="badge rounded-pill bg-secondary tag">janusxr</a>
</td>
<td>
This generates an JanusXR address.<br>
<a href="https://janusxr.org/" target="_blank">JanusXR</a> is an Open Immersive Web-layer since 2015, which allows existing webpages to become spatial.<br>
It is Free and Opensource, and allows you to meet others in this experience (avatars, chat and voice etc).<br>
</td>
</tr>
<tr>
<td>
<a class="badge rounded-pill bg-secondary tag">mml</a>
</td>
<td>
This generates an MML address.<br>
Metaverse Markup Language (<a href="https://mml.io" target="_blank">MML</a>) is an open markup language used to define experiences.
</td>
</tr>
</table>

View file

@ -81,32 +81,72 @@
</tr> </tr>
<% end %> <% end %>
<% if ENV['FEDERATE_DRIVE_HOST'].present? %> <% if ENV['FEDERATE_DRIVE_HOST'].present? %>
<% if @model.tags.where(name: "janusxr" ).any? %>
<tr>
<td>
<i class="bi bi-eye-fill" role="img"></i>
</td>
<td>
<%
fediURL = ENV['FEDERATE_DRIVE_HOST']+"/"+@model.library.name+"/"+@model.path.gsub("#","%23")+"/.xrforge/janusxr.html"
%>
<%= link_to "JanusXR address", fediURL, target:"_blank" %>
<a href="" target="_blank"><i class="bi bi-link-45deg"></i></a>
<label for="toggle_janusxr"><i class="bi bi-info-circle"></i></label>
<div class="toggle-box">
<input type="checkbox" id="toggle_janusxr" hidden>
<div class="hidden-tooltip">
<i class="bi bi-arrow-90deg-up"></i>&nbsp;
<small>
This is the JanusXR address.<br>
<a href="https://janusxr.org/" target="_blank">JanusXR</a> is an Open Immersive Web-layer since 2015, which allows existing webpages to become spatial.<br>
It is Free and Opensource, and allows you to meet others in this experience (avatars, chat and voice etc).<br>
</small>
</div>
</div>
</td>
</tr>
<%
# lets generate a JanusXR spatial layer for the current scene
jmlfile = @model.library.path+"/"+@model.path+"/.xrforge/scene.jml"
jml = ""
if File.exist?(jmlfile)
jml = File.read(jmlfile)
end
%>
<!-- JML (https://janusxr.org) spatial markup
<%== jml %>
-->
<% end %>
<% if @model.tags.where(name: "mml" ).any? %>
<tr> <tr>
<td> <td>
<i class="bi bi-people" role="img"></i> <i class="bi bi-eye-fill" role="img"></i>
</td> </td>
<td> <td>
<%= link_to "JanusXR Metaverse", ENV['FEDERATE_DRIVE_HOST']+"/"+@model.library.name+"/"+@model.path.gsub("#","%23")+"/.xrforge/janusxr.html" %> <%= link_to "MML address", "https://viewer.mml.io/main/v1/?url="+URI.encode_www_form_component(ENV['FEDERATE_DRIVE_HOST']+"/"+@model.library.name+"/"+@model.path.gsub("#","%23")+"/.xrforge/scene.mml"), target:"_blank" %>
<label for="toggle_janusxr"><i class="bi bi-info-circle"></i></label> <a href="<%= ENV['FEDERATE_DRIVE_HOST']+"/"+@model.library.name+"/"+@model.path.gsub("#","%23")+"/.xrforge/scene.mml" %>" target="_blank"><i class="bi bi-link-45deg"></i></a>
<label for="toggle_mml"><i class="bi bi-info-circle"></i></label>
<div class="toggle-box"> <div class="toggle-box">
<input type="checkbox" id="toggle_janusxr" hidden> <input type="checkbox" id="toggle_mml" hidden>
<div class="hidden-tooltip"> <div class="hidden-tooltip">
<i class="bi bi-arrow-90deg-up"></i>&nbsp; <i class="bi bi-arrow-90deg-up"></i>&nbsp;
<small> <small>
This is the JanusXR address.<br> This is the MML address.<br>
<a href="https://janusxr.org/" target="_blank">JanusXR</a> is an established Metaverse since 2015.<br> Metaverse Markup Language (<a href="https://mml.io" target="_blank">MML</a>) is an open markup language used to define experiences.
It is Free and Opensource, and allows you to meet others in this experience (avatars, chat and voice etc).
</small> </small>
</div> </div>
</div> </div>
</td> </td>
</tr> </tr>
<tr> <% end %>
<td>
<i class="bi bi-file-zip" role="img"></i>
</td>
<td><%= link_to "zip archive", "/"+@model.library.name+"/"+@model.path.gsub("#","%23")+"/.xrforge/experience.zip" %></td>
</tr>
<tr> <tr>
<td> <td>
<i class="bi bi-journal-check" role="img"></i> <i class="bi bi-journal-check" role="img"></i>
@ -121,19 +161,7 @@
<small> <small>
This is the build log of XR Forge.<br> This is the build log of XR Forge.<br>
When you add files, they are processed, validated (for <a href="https://xrfragment.org" target="_blank">XR Fragment</a> compliance).<br> When you add files, they are processed, validated (for <a href="https://xrfragment.org" target="_blank">XR Fragment</a> compliance).<br>
But also features can be toggled via tags:<br> <% render 'models/tags_info' %>
<br>
<table class="table">
<tr>
<td>
<a class="badge rounded-pill bg-secondary tag">menu</a>
</td>
<td>
This will generate a navigator-menu <b>into</b> your main 3D file.<br>
The links can be edited <%= link_to "here", edit_model_path(@model) %>
</td>
</tr>
</table>
</small> </small>
</div> </div>
</div> </div>
@ -161,6 +189,7 @@
</td> </td>
</tr> </tr>
<% end %> <% end %>
<% if @model.collection %> <% if @model.collection %>
<tr> <tr>
<td><%= icon "collection", Collection.model_name.human(count: 100) %></td> <td><%= icon "collection", Collection.model_name.human(count: 100) %></td>
@ -202,7 +231,18 @@
</tr> </tr>
<tr> <tr>
<td><%= icon "tag", t(".tags") %></td> <td><%= icon "tag", t(".tags") %></td>
<td><%= render "tag_list", tags: @model.tags.order(taggings_count: :desc, name: :asc), filter: @filter %></td> <td><%= render "tag_list", tags: @model.tags.order(taggings_count: :desc, name: :asc), filter: @filter %>
<label for="toggle_tags"><i class="bi bi-info-circle"></i></label>
<div class="toggle-box">
<input type="checkbox" id="toggle_tags" hidden>
<div class="hidden-tooltip" style="max-height:400px">
<i class="bi bi-arrow-90deg-up"></i>&nbsp;
<small>
<% render 'models/tags_info' %>
</small>
</div>
</div>
</td>
</tr> </tr>
<% if SiteSettings.social_enabled? %> <% if SiteSettings.social_enabled? %>
<tr> <tr>

View file

@ -76,6 +76,7 @@ rec
pkgs.pkgsStatic.inotify-tools # inotifywait e.g. pkgs.pkgsStatic.inotify-tools # inotifywait e.g.
pkgs.pkgsStatic.zip # inotifywait e.g. pkgs.pkgsStatic.zip # inotifywait e.g.
pkgs.pkgsStatic.ts # job management pkgs.pkgsStatic.ts # job management
pkgs.janus-gateway # webrtc server
myAssimp # cli 3D editing/conversion myAssimp # cli 3D editing/conversion
./.. ./..
]; ];