extract (non-user) textures from glb + import user texture into glb

This commit is contained in:
Leon van Kammen 2025-11-28 10:04:56 +01:00
parent 43ff366ca4
commit e9b6500579
6 changed files with 175 additions and 106 deletions

View file

@ -0,0 +1,83 @@
#!/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]
require 'base64'
require 'json'
begin
# Change the directory
dir = File.dirname(filename)
Dir.chdir( dir )
# Read and parse the JSON file
data = JSON.parse( File.read( "datapackage.json" ) )
logfile = File.join( File.dirname(filename), ".xrforge/log.txt" )
data['resources'].each do |resource|
ext = File.extname(resource['path'])
filenameWithoutExt = File.basename(resource['path'], ext)
if ! XRForge::MODEL_EXT.any?( ext )
next # skip unknown 3d files
end
if ! resource['path'].match(/^[a-zA-Z0-9]/)
next # skip filenames like '_generated.glb' e.g. (produced by hooks)
end
XRForge.log("✅ unpacking textures", logfile)
gltf_path = ".xrforge/#{filenameWithoutExt}.gltf"
system("assimp export #{resource['path']} #{gltf_path} --embed-textures")
Dir.chdir("#{dir}/.xrforge")
system("assimp extract #{filenameWithoutExt}.gltf | sed 's|/.*/||g'")
Dir.chdir(dir)
# Read and parse the GLTF JSON
gltf = JSON.parse(File.read(gltf_path))
# Ensure images section exists
unless gltf['images'] && gltf['images'].is_a?(Array)
puts "✅ No 'images' array found in #{gltf_path}"
next
end
# Iterate through the images array
gltf['images'].each_with_index do |img, i|
name = img['name']
next unless name && !name.empty?
old_filename = "#{dir}/.xrforge/#{filenameWithoutExt}_img#{i}.png"
new_filename = "#{dir}/#{name}.png"
puts old_filename
puts new_filename
# move file to modeldirectory (but dont overwrite if user overwrite it)
if File.exist?(old_filename) && !File.exist?(new_filename)
puts "✅ Renaming #{old_filename} -> #{new_filename}"
File.rename(old_filename, new_filename)
else
puts "✅ Not overwriting (useruploaded) #{new_filename}"
end
end
end
XRForge.log(" ", logfile)
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,80 @@
#!/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]
require 'base64'
require 'json'
begin
# Change the directory
dir = File.dirname(filename)
Dir.chdir( dir )
# Read and parse the JSON file
data = JSON.parse( File.read( "datapackage.json" ) )
logfile = File.join( File.dirname(filename), ".xrforge/log.txt" )
update = false
data['resources'].each do |resource|
ext = File.extname(resource['path'])
filenameWithoutExt = File.basename(resource['path'], ext)
if ! XRForge::MODEL_EXT.any?( ext )
next # skip unknown 3d files
end
if ! resource['path'].match(/^[a-zA-Z0-9]/)
next # skip filenames like '_generated.glb' e.g. (produced by hooks)
end
XRForge.log("✅ compiling textures", logfile)
gltf_path = ".xrforge/#{filenameWithoutExt}.gltf"
# Read and parse the GLTF JSON
gltf = JSON.parse(File.read(gltf_path))
# Ensure images section exists
unless gltf['images'] && gltf['images'].is_a?(Array)
XRForge.log("✅ No 'images' array found in #{gltf_path}",logfile)
next
end
# Iterate through the images array
gltf['images'].each_with_index do |img, i|
name = img['name']
next unless name && !name.empty?
new_filename = "#{dir}/#{name}.png"
# move file to modeldirectory (but dont overwrite if user overwrite it)
XRForge.log("🤔 checking #{new_filename}",logfile)
if File.exist?(new_filename)
XRForge.log("✅ importing #{new_filename} -> #{resource['path']}",logfile)
img['uri'] = "#{name}.png"
update = true
end
end
if update
File.write( gltf_path, JSON.pretty_generate(gltf) )
XRForge.log("✅ writing #{resource['path']}",logfile)
system("assimp export #{gltf_path} #{resource['path']} --embed-textures")
end
end
XRForge.log(" ", logfile)
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

@ -1,101 +0,0 @@
#!/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
puts "TODO"
exit 0
filename = ARGV[0]
require 'base64'
require 'json'
# Updates the 'uri' of an image in a GLTF hash when a matching PNG filename is found.
#
# gltf: a parsed JSON hash from a .gltf file
# png_path: path to the PNG file to embed
#
# returns: true if updated successfully, false if not found
def update_gltf_image(gltf, png_path)
# Get base name (without extension)
name = File.basename(png_path, '.png')
# Find image entry with the same name
image_entry = gltf['images']&.find { |img| img['name'] == name }
unless image_entry
warn "No image named '#{name}' found in GLTF"
return false
end
# Read and base64-encode the PNG file
data = File.binread(png_path)
encoded = Base64.strict_encode64(data)
# Update the URI field with the base64-encoded PNG data URI
image_entry['uri'] = "data:image/png;base64,#{encoded}"
puts "Updated image '#{name}' in GLTF"
true
end
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" ) )
ext = File.extname(filename)
filenameWithoutExt = File.basename(filename, ext)
if ! XRForge::MODEL_EXT.any?( ext )
exit(0) # not a 3d file
end
logfile = File.join( File.dirname(filename), ".xrforge/log.txt" )
XRForge.log("✅ unpacking textures", logfile)
gltf_path = ".xrforge/#{filenameWithoutExt}.gltf"
system("assimp export #{filename} #{gltf_path}")
system("assimp extract #{filename} | sed 's|/.*/||g'")
# Read and parse the GLTF JSON
gltf = JSON.parse(File.read(gltf_path))
# Ensure images section exists
unless gltf['images'] && gltf['images'].is_a?(Array)
abort("No 'images' array found in #{gltf_path}")
end
# Iterate through the images array
gltf['images'].each_with_index do |img, i|
name = img['name']
next unless name && !name.empty?
old_filename = "website_img#{i}.png"
new_filename = "#{name}.png"
if File.exist?(old_filename)
XRForge.log("✅ Renaming #{old_filename} -> #{new_filename}", logfile)
File.rename(old_filename, new_filename)
end
end
XRForge.log(" ", logfile)
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

@ -49,10 +49,12 @@ begin
if model_file
XRForge.log("✅ Final model file: '#{model_file}'", logfile)
# update datapackage
data['main'] = model_file
if ! data['keywords'].include?('xrfragments')
data['keywords'].push('xrfragments')
File.write("datapackage.json", JSON.pretty_generate(data) )
end
File.write("datapackage.json", JSON.pretty_generate(data) )
system("assimp export #{model_file} .xrforge/scene.gltf")
else
XRForge.log("❌ No suitable 3D file found for XR Fragments-compatible experience", logfile)
# update datapackage

View file

@ -67,8 +67,8 @@ begin
data = JSON.parse( File.read( "datapackage.json" ) )
logfile = File.join( File.dirname(filename), ".xrforge/log.txt" )
data['keywords'].any? do |tag|
if tag.match?(/@.*@.*\./) # scan for activitypub handle (@foo@mastodon.online e.g.)
data['keywords'].each do |tag|
if tag.match?(/^@.*@.*\./) # scan for activitypub handle (@foo@mastodon.online e.g.)
generate(tag,filename,dir,data,logfile)
end
end

View file

@ -3,17 +3,22 @@ test -n "$5" || { echo "usage: ./mastodon-post.sh <text> <title> <link> <handle>
out="$5"
tmpdir=/tmp/.mastodon-post
outdir="$(dirname "$out")"
mydir="$(dirname $(readlink -f $0))"
bgfile="$outdir"/mastodon-post-bg.png
cd $mydir
set -x
# create tmp workspace
test -d $tmpdir && rm -rf $tmpdir
mkdir $tmpdir
cp * $tmpdir/.
# use template PNG from modeldir
test -f "$bgfile" || cp $tmpdir/mastodon-post-bg.png "$bgfile"
cp "$bgfile" $tmpdir/.
rm $tmpdir/mastodon-post.png # IMPORTANT: prevent newer/older imagemagick -clobber (non)existence
set -x
magick $tmpdir/mastodon-post-bg.png \
magick "$bgfile" \
\( -size 580x -background none -pointsize 32 -interline-spacing 10 -fill \#FFF -font Montserrat-SemiBold.ttf "caption:$1" \) \
-gravity center -composite \
-pointsize 20 \