Compare commits

..

No commits in common. "54efe0da3d283f3706f5f1fd23a7a2ea113d394a" and "c29a748151635adce6ff8439d9a60dbfa1e899d8" have entirely different histories.

7 changed files with 138 additions and 256 deletions

View file

@ -14,6 +14,36 @@ filename = ARGV[0]
require 'base64' require 'base64'
require 'json' 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 begin
# Change the directory # Change the directory
@ -25,20 +55,36 @@ begin
ext = File.extname(filename) ext = File.extname(filename)
filenameWithoutExt = File.basename(filename, ext) filenameWithoutExt = File.basename(filename, ext)
if ! XRForge::MODEL_EXT.any?( ext ) || ext == ".gltf" if ! XRForge::MODEL_EXT.any?( ext )
exit(0) # not a 3d file exit(0) # not a 3d file
end end
logfile = File.join( File.dirname(filename), ".xrforge/log.txt" ) logfile = File.join( File.dirname(filename), ".xrforge/log.txt" )
XRForge.log("✅ generating gltf", logfile) XRForge.log("✅ generating gltf", logfile)
gltf_path = ".xrforge/scene.gltf" gltf_path = ".xrforge/#{filenameWithoutExt}.gltf"
system("assimp export #{filename} #{gltf_path}") system("assimp export #{filename} #{gltf_path}")
system("assimp extract #{filename} | sed 's|/.*/||g'") system("assimp extract #{filename} | sed 's|/.*/||g'")
# tag it! # Read and parse the GLTF JSON
if ! data['keywords'].include?('gltf') gltf = JSON.parse(File.read(gltf_path))
data['keywords'].push('gltf')
File.write("datapackage.json", JSON.pretty_generate(data) ) # 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 end
XRForge.log(" ", logfile) XRForge.log(" ", logfile)

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

@ -5,7 +5,7 @@ require_relative './../../xrforge.rb'
# Check if a filename is provided # Check if a filename is provided
if ARGV.length != 1 if ARGV.length != 1
puts "Usage: #{$0} <path/to/experience/datapackage.json>" puts "Usage: #{$0} <path/to/experience/somefile.xxx>"
exit 1 exit 1
end end
@ -13,11 +13,6 @@ filename = ARGV[0]
begin begin
# dont run for each file-update
if ! filename.end_with?("datapackage.json")
exit 0
end
# Change the directory # Change the directory
dir = File.dirname(filename) dir = File.dirname(filename)
Dir.chdir( File.dirname(filename) ) Dir.chdir( File.dirname(filename) )
@ -73,7 +68,7 @@ begin
<Assets> <Assets>
<assetobject id="experience" src="#{federate_drive_host}/#{model_file.gsub("#","%23")}"/> <assetobject id="experience" src="#{federate_drive_host}/#{model_file.gsub("#","%23")}"/>
</Assets> </Assets>
<Room pbr="true" #{ data['keywords'].include?('singleuser') ? "private='true'" : ""}> <Room #{ data['keywords'].include?('singleuser') ? "private='true'" : ""}>
<object pos="0 0 0" collision_id="experience" id="experience" /> <object pos="0 0 0" collision_id="experience" id="experience" />
</Room> </Room>
</FireBoxRoom> </FireBoxRoom>

View file

@ -11,11 +11,6 @@ end
filename = ARGV[0] filename = ARGV[0]
# dont run for each file-update
if ! filename.end_with?("datapackage.json")
exit 0
end
begin begin
# Change the directory # Change the directory

View file

@ -1,96 +0,0 @@
#!/usr/bin/env ruby
require 'json'
require 'rss'
require 'open-uri'
require 'cgi'
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]
# dont run for each file-update
if ! filename.end_with?("datapackage.json")
exit 0
end
begin
# Change the directory
Dir.chdir( File.dirname(filename) )
# Read and parse the JSON file
data = JSON.parse( File.read( "datapackage.json" ) )
data['keywords'].any? do |tag|
if tag.match?(/@.*@.*\./) # scan for activitypub handle (@foo@mastodon.online e.g.)
APHandle = tag
end
end
if ! APHandle # nothing to do
exit 0
end
APHandle = "@vladh@merveilles.town"
logfile = File.join( File.dirname(filename), ".xrforge/log.txt" )
XRForge.log("✅ starting Mastodon post2image", logfile)
parts = APHandle.split("@")
server = parts[2].sub(/@/,"")
rssUrl = "https://#{server}/@#{parts[1]}.rss"
XRForge.log("✅ checking #{rssUrl}", logfile)
feed = RSS::Parser.parse(URI.open(rssUrl, 'User-Agent' => 'Ruby-RSS-Client'))
puts feed.channel.title
puts feed.channel.link
first_item = feed.items.first
if first_item
description = CGI.unescapeHTML(first_item.description.to_s).gsub(/<[^>]*>/, '')
description = description.length > 150 ? description = description[0,200] + " (..)" : description
else
XRForge.log("❌ did not find post", logfile)
exit 0
end
puts description
# look for first image
MEDIA_REGEX = /<media:content url=['"]([^'"]+)['"]/
img = feed.to_s.match(MEDIA_REGEX)
if img and img[1] and img[1].match(/(png|jpg)/)
imgurl = img[1]
end
text = "\n\"#{description}\"\n\n~ #{feed.channel.title}\n#{feed.channel.link}"
textFile = ".xrforge/mastodon-post.txt"
File.open(textFile,'w') do |file|
file.puts text
end
cmd = "magick -size 800x800 -background white -pointsize 48 -interline-spacing 10 -fill \\#555 -gravity center -font /usr/local/lib/ruby/3.4.0/rdoc/generator/template/darkfish/fonts/Lato-Regular.ttf caption:@#{textFile} mastodon-post.png"
puts cmd
system(cmd)
XRForge.log(" ", logfile)
rescue OpenURI::HTTPError => e
# Handle HTTP errors (e.g., 404 not found, 403 forbidden)
puts "Error fetching feed: #{e.message}"
exit
rescue => e
# Handle other parsing or connection errors
puts "An error occurred: #{e.message}"
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

@ -187,28 +187,6 @@
</div> </div>
</td> </td>
</tr> </tr>
<tr>
<td>
<i class="bi bi-filetype-json" role="img"></i>
</td>
<td>
<%= link_to "glTF JSON", ENV['FEDERATE_DRIVE_HOST']+"/"+@model.library.name+"/"+@model.path.gsub("#","%23")+"/.xrforge/scene.gltf" %>
<label for="toggle_gltf"><i class="bi bi-info-circle"></i></label>
<div class="toggle-box">
<input type="checkbox" id="toggle_gltf" hidden>
<div class="hidden-tooltip">
<i class="bi bi-arrow-90deg-up"></i>&nbsp;
<small>
This is the glTF JSON URL.<br>
This is an opensource fallback-mechanism for developers/engine's which don't support <a href="https://xfragment.org" target="_blank">XR Fragments</a>.
deeplinks.<br><br>
Instead of <b>https://my.org/foo.glb#chair</b> they can do:
<code><pre>$ curl <gltfURL> | jq '.meshes[] | select(.name == "chair")'</pre></code>
</small>
</div>
</div>
</td>
</tr>
<% end %> <% end %>
<% if @model.collection %> <% if @model.collection %>

View file

@ -2,16 +2,43 @@
<!-- Created with Inkscape (http://www.inkscape.org/) --> <!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg <svg
width="128.27412mm" width="128.27411mm"
height="145.65482mm" height="145.65482mm"
viewBox="0 0 128.27413 145.65482" viewBox="0 0 128.27411 145.65482"
version="1.1" version="1.1"
id="svg5" id="svg5"
xml:space="preserve" xml:space="preserve"
inkscape:export-filename="../../../../../Downloads/xrforge.svg"
inkscape:export-xdpi="96"
inkscape:export-ydpi="96"
sodipodi:docname="logo.svg"
inkscape:dataloss="true"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"><defs xmlns:svg="http://www.w3.org/2000/svg"><sodipodi:namedview
id="namedview7"
pagecolor="#505050"
bordercolor="#ffffff"
borderopacity="1"
inkscape:showpageshadow="0"
inkscape:pageopacity="0"
inkscape:pagecheckerboard="1"
inkscape:deskcolor="#505050"
inkscape:document-units="mm"
showgrid="false"
inkscape:zoom="0.56323837"
inkscape:cx="226.36952"
inkscape:cy="409.24058"
inkscape:window-width="1920"
inkscape:window-height="1030"
inkscape:window-x="0"
inkscape:window-y="26"
inkscape:window-maximized="1"
inkscape:current-layer="layer1" /><defs
id="defs2"><linearGradient id="defs2"><linearGradient
inkscape:collect="always"
id="linearGradient12286"><stop id="linearGradient12286"><stop
style="stop-color:#ea0bfe;stop-opacity:0.11569338;" style="stop-color:#ea0bfe;stop-opacity:0.11569338;"
offset="0" offset="0"
@ -19,6 +46,7 @@
style="stop-color:#ffffff;stop-opacity:0;" style="stop-color:#ffffff;stop-opacity:0;"
offset="1" offset="1"
id="stop12284" /></linearGradient><linearGradient id="stop12284" /></linearGradient><linearGradient
inkscape:collect="always"
id="linearGradient12159"><stop id="linearGradient12159"><stop
style="stop-color:#fe83ff;stop-opacity:0.35045233;" style="stop-color:#fe83ff;stop-opacity:0.35045233;"
offset="0" offset="0"
@ -26,6 +54,7 @@
style="stop-color:#3c9cff;stop-opacity:0.32712477;" style="stop-color:#3c9cff;stop-opacity:0.32712477;"
offset="1" offset="1"
id="stop12157" /></linearGradient><linearGradient id="stop12157" /></linearGradient><linearGradient
inkscape:collect="always"
id="linearGradient12153"><stop id="linearGradient12153"><stop
style="stop-color:#fe83ff;stop-opacity:0.29342434;" style="stop-color:#fe83ff;stop-opacity:0.29342434;"
offset="0" offset="0"
@ -33,6 +62,7 @@
style="stop-color:#3c9cff;stop-opacity:0.31577286;" style="stop-color:#3c9cff;stop-opacity:0.31577286;"
offset="1" offset="1"
id="stop12151" /></linearGradient><linearGradient id="stop12151" /></linearGradient><linearGradient
inkscape:collect="always"
id="linearGradient12139"><stop id="linearGradient12139"><stop
style="stop-color:#ea0bfe;stop-opacity:0.50826901;" style="stop-color:#ea0bfe;stop-opacity:0.50826901;"
offset="0" offset="0"
@ -40,6 +70,7 @@
style="stop-color:#ffffff;stop-opacity:0;" style="stop-color:#ffffff;stop-opacity:0;"
offset="1" offset="1"
id="stop12137" /></linearGradient><linearGradient id="stop12137" /></linearGradient><linearGradient
inkscape:collect="always"
id="linearGradient12102"><stop id="linearGradient12102"><stop
style="stop-color:#fe83ff;stop-opacity:1;" style="stop-color:#fe83ff;stop-opacity:1;"
offset="0" offset="0"
@ -47,6 +78,7 @@
style="stop-color:#3c9cff;stop-opacity:0.81848603;" style="stop-color:#3c9cff;stop-opacity:0.81848603;"
offset="1" offset="1"
id="stop12100" /></linearGradient><linearGradient id="stop12100" /></linearGradient><linearGradient
inkscape:collect="always"
id="linearGradient7688"><stop id="linearGradient7688"><stop
style="stop-color:#ffffff;stop-opacity:1;" style="stop-color:#ffffff;stop-opacity:1;"
offset="0" offset="0"
@ -68,6 +100,7 @@
x2="273.12695" x2="273.12695"
y2="24.048252" y2="24.048252"
gradientUnits="userSpaceOnUse" /><linearGradient gradientUnits="userSpaceOnUse" /><linearGradient
inkscape:collect="always"
xlink:href="#linearGradient7688" xlink:href="#linearGradient7688"
id="linearGradient7692" id="linearGradient7692"
x1="115.42191" x1="115.42191"
@ -75,6 +108,7 @@
x2="117.16759" x2="117.16759"
y2="131.87457" y2="131.87457"
gradientUnits="userSpaceOnUse" /><linearGradient gradientUnits="userSpaceOnUse" /><linearGradient
inkscape:collect="always"
xlink:href="#linearGradient12102" xlink:href="#linearGradient12102"
id="linearGradient12104" id="linearGradient12104"
x1="54.029213" x1="54.029213"
@ -82,6 +116,7 @@
x2="176.85757" x2="176.85757"
y2="71.733955" y2="71.733955"
gradientUnits="userSpaceOnUse" /><linearGradient gradientUnits="userSpaceOnUse" /><linearGradient
inkscape:collect="always"
xlink:href="#linearGradient12159" xlink:href="#linearGradient12159"
id="linearGradient12108" id="linearGradient12108"
gradientUnits="userSpaceOnUse" gradientUnits="userSpaceOnUse"
@ -90,6 +125,7 @@
x2="176.85757" x2="176.85757"
y2="71.733955" y2="71.733955"
gradientTransform="translate(0,4.7625002)" /><linearGradient gradientTransform="translate(0,4.7625002)" /><linearGradient
inkscape:collect="always"
xlink:href="#linearGradient12153" xlink:href="#linearGradient12153"
id="linearGradient12112" id="linearGradient12112"
gradientUnits="userSpaceOnUse" gradientUnits="userSpaceOnUse"
@ -98,6 +134,7 @@
y1="71.733955" y1="71.733955"
x2="176.85757" x2="176.85757"
y2="71.733955" /><linearGradient y2="71.733955" /><linearGradient
inkscape:collect="always"
xlink:href="#linearGradient12286" xlink:href="#linearGradient12286"
id="linearGradient12141" id="linearGradient12141"
x1="137.33427" x1="137.33427"
@ -106,6 +143,7 @@
y2="88.766113" y2="88.766113"
gradientUnits="userSpaceOnUse" gradientUnits="userSpaceOnUse"
gradientTransform="matrix(1.2850723,0,0,1.2367478,-50.791853,-16.999519)" /><linearGradient gradientTransform="matrix(1.2850723,0,0,1.2367478,-50.791853,-16.999519)" /><linearGradient
inkscape:collect="always"
xlink:href="#linearGradient12139" xlink:href="#linearGradient12139"
id="linearGradient12239" id="linearGradient12239"
gradientUnits="userSpaceOnUse" gradientUnits="userSpaceOnUse"
@ -115,8 +153,9 @@
y2="88.766113" y2="88.766113"
gradientTransform="matrix(-1.2669282,0,0,1.2603766,278.3952,-19.18513)" /><filter gradientTransform="matrix(-1.2669282,0,0,1.2603766,278.3952,-19.18513)" /><filter
style="color-interpolation-filters:sRGB" style="color-interpolation-filters:sRGB"
inkscape:label="Drop Shadow"
id="filter12743" id="filter12743"
x="-0.079006466" x="-0.079006463"
y="-0.2479955" y="-0.2479955"
width="1.1580434" width="1.1580434"
height="1.4959902"><feFlood height="1.4959902"><feFlood
@ -143,11 +182,12 @@
result="composite2" result="composite2"
id="feComposite12741" /></filter><filter id="feComposite12741" /></filter><filter
style="color-interpolation-filters:sRGB" style="color-interpolation-filters:sRGB"
inkscape:label="Drop Shadow"
id="filter13745" id="filter13745"
x="-0.13442897" x="-0.13448349"
y="-0.73597233" y="-0.73597109"
width="1.2688579" width="1.268967"
height="2.4719447"><feFlood height="2.4719422"><feFlood
flood-opacity="1" flood-opacity="1"
flood-color="rgb(26,135,255)" flood-color="rgb(26,135,255)"
result="flood" result="flood"
@ -170,6 +210,7 @@
operator="over" operator="over"
result="composite2" result="composite2"
id="feComposite13743" /></filter><linearGradient id="feComposite13743" /></filter><linearGradient
inkscape:collect="always"
xlink:href="#linearGradient12286" xlink:href="#linearGradient12286"
id="linearGradient14475" id="linearGradient14475"
gradientUnits="userSpaceOnUse" gradientUnits="userSpaceOnUse"
@ -178,22 +219,41 @@
y1="88.766113" y1="88.766113"
x2="177.37935" x2="177.37935"
y2="88.766113" /></defs><g y2="88.766113" /></defs><g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1" id="layer1"
transform="translate(-51.358538,-4.8451999)"><path transform="translate(-51.358538,-4.8451999)"><path
sodipodi:type="star"
style="fill:#000000;fill-opacity:1;stroke:#ffffff;stroke-width:2.3;stroke-dasharray:none" style="fill:#000000;fill-opacity:1;stroke:#ffffff;stroke-width:2.3;stroke-dasharray:none"
id="path1638-3" id="path1638-3"
inkscape:flatsided="true"
sodipodi:sides="6"
sodipodi:cx="138.75616"
sodipodi:cy="263.41873"
sodipodi:r1="70.000412"
sodipodi:r2="60.622131"
sodipodi:arg1="-2.6179939"
sodipodi:arg2="-2.0943951"
inkscape:rounded="0"
inkscape:randomized="0"
d="m 78.134029,228.41853 60.622131,-35.00021 60.62214,35.0002 0,70.00042 -60.62213,35.0002 -60.62214,-35.0002 z" d="m 78.134029,228.41853 60.622131,-35.00021 60.62214,35.0002 0,70.00042 -60.62213,35.0002 -60.62214,-35.0002 z"
inkscape:transform-center-x="-4.0923148e-06"
inkscape:transform-center-y="-2.6621219e-06"
transform="matrix(1.0382846,0,0,1.0210168,-28.572793,-191.28234)" /><g transform="matrix(1.0382846,0,0,1.0210168,-28.572793,-191.28234)" /><g
id="g12126"><path id="g12126"
inkscape:label="lines"><path
style="fill:none;fill-opacity:1;stroke:url(#linearGradient12104);stroke-width:0.5;stroke-dasharray:none;stroke-opacity:1" style="fill:none;fill-opacity:1;stroke:url(#linearGradient12104);stroke-width:0.5;stroke-dasharray:none;stroke-opacity:1"
d="M 54.029427,71.745371 176.85736,71.722537" d="M 54.029427,71.745371 176.85736,71.722537"
id="path12042" /><path id="path12042"
inkscape:label="line" /><path
style="fill:none;fill-opacity:1;stroke:url(#linearGradient12108);stroke-width:0.6;stroke-dasharray:none;stroke-opacity:1" style="fill:none;fill-opacity:1;stroke:url(#linearGradient12108);stroke-width:0.6;stroke-dasharray:none;stroke-opacity:1"
d="M 54.029427,76.507874 176.85736,76.48504" d="M 54.029427,76.507874 176.85736,76.48504"
id="path12106" /><path id="path12106"
inkscape:label="line" /><path
style="fill:none;fill-opacity:1;stroke:url(#linearGradient12112);stroke-width:0.8;stroke-dasharray:none;stroke-opacity:1" style="fill:none;fill-opacity:1;stroke:url(#linearGradient12112);stroke-width:0.8;stroke-dasharray:none;stroke-opacity:1"
d="M 54.029427,91.32455 176.85736,91.301716" d="M 54.029427,91.32455 176.85736,91.301716"
id="path12110" /><path id="path12110"
inkscape:label="line" /><path
style="fill:url(#linearGradient12239);fill-opacity:1;stroke:none;stroke-width:2.416;stroke-dasharray:none;stroke-opacity:1" style="fill:url(#linearGradient12239);fill-opacity:1;stroke:none;stroke-width:2.416;stroke-dasharray:none;stroke-opacity:1"
d="M 104.40252,71.315371 53.668551,108.90527 v 5.16648 z" d="M 104.40252,71.315371 53.668551,108.90527 v 5.16648 z"
id="path12133-5" /><path id="path12133-5" /><path
@ -202,10 +262,12 @@
id="path12133" /><path id="path12133" /><path
style="fill:url(#linearGradient12141);fill-opacity:1;stroke:none;stroke-width:2.41042;stroke-dasharray:none;stroke-opacity:1" style="fill:url(#linearGradient12141);fill-opacity:1;stroke:none;stroke-width:2.41042;stroke-dasharray:none;stroke-opacity:1"
d="m 115.37951,70.94784 5.14781,74.3852 c 0,0 -4.42958,5.94658 -10.22727,-0.9757 z" d="m 115.37951,70.94784 5.14781,74.3852 c 0,0 -4.42958,5.94658 -10.22727,-0.9757 z"
id="path12280" /></g><path id="path12280"
sodipodi:nodetypes="cccc" /></g><path
style="fill:url(#linearGradient7692);fill-opacity:1;stroke:none;stroke-width:2.3;stroke-dasharray:none;stroke-opacity:1" style="fill:url(#linearGradient7692);fill-opacity:1;stroke:none;stroke-width:2.3;stroke-dasharray:none;stroke-opacity:1"
d="M 53.906377,71.659657 H 177.09207 l 0.24519,40.701533 -61.93861,35.18517 -61.54407,-35.3442 z" d="M 53.906377,71.659657 H 177.09207 l 0.24519,40.701533 -61.93861,35.18517 -61.54407,-35.3442 z"
id="path7625" /><g id="path7625"
sodipodi:nodetypes="cccccc" /><g
id="g4494" id="g4494"
transform="matrix(2.7825702,0,0,3.2095953,58.857189,44.497537)" transform="matrix(2.7825702,0,0,3.2095953,58.857189,44.497537)"
style="filter:url(#filter12743)"><g style="filter:url(#filter12743)"><g
@ -238,6 +300,7 @@
x="69.809654" x="69.809654"
y="107.18471" y="107.18471"
id="text1004"><tspan id="text1004"><tspan
sodipodi:role="line"
id="tspan1002" id="tspan1002"
style="font-style:normal;font-variant:normal;font-weight:100;font-stretch:normal;font-size:19.7556px;font-family:Montserrat;-inkscape-font-specification:'Montserrat Thin';stroke-width:0.5;stroke-dasharray:none" style="font-style:normal;font-variant:normal;font-weight:100;font-stretch:normal;font-size:19.7556px;font-family:Montserrat;-inkscape-font-specification:'Montserrat Thin';stroke-width:0.5;stroke-dasharray:none"
x="69.809654" x="69.809654"
@ -247,16 +310,18 @@
x="160.00148" x="160.00148"
y="107.33738" y="107.33738"
id="text1004-3"><tspan id="text1004-3"><tspan
sodipodi:role="line"
id="tspan1002-6" id="tspan1002-6"
style="font-style:normal;font-variant:normal;font-weight:100;font-stretch:normal;font-size:19.7556px;font-family:Montserrat;-inkscape-font-specification:'Montserrat Thin';stroke-width:0.5;stroke-dasharray:none" style="font-style:normal;font-variant:normal;font-weight:100;font-stretch:normal;font-size:19.7556px;font-family:Montserrat;-inkscape-font-specification:'Montserrat Thin';stroke-width:0.5;stroke-dasharray:none"
x="160.00148" x="160.00148"
y="107.33738">]</tspan></text><text y="107.33738">]</tspan></text><text
xml:space="preserve" xml:space="preserve"
style="font-weight:100;font-size:16.2278px;font-family:Montserrat;-inkscape-font-specification:'Montserrat Thin';text-align:center;letter-spacing:0.529167px;writing-mode:tb-rl;text-orientation:upright;text-anchor:middle;fill:#020202;fill-opacity:1;stroke:#000000;stroke-width:2.3;stroke-dasharray:none;stroke-opacity:1;filter:url(#filter13745)" style="font-weight:100;font-size:16.2278px;font-family:Montserrat;-inkscape-font-specification:'Montserrat Thin';text-align:center;letter-spacing:0.529167px;writing-mode:tb-rl;text-orientation:upright;text-anchor:middle;fill:#020202;fill-opacity:1;stroke:#000000;stroke-width:2.3;stroke-dasharray:none;stroke-opacity:1;filter:url(#filter13745)"
x="75.235199" x="85.100029"
y="106.55447" y="91.992111"
id="text3268"><tspan id="text3268"><tspan
sodipodi:role="line"
id="tspan3266" id="tspan3266"
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:16.2278px;font-family:Montserrat;-inkscape-font-specification:'Montserrat Bold';text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:2.3;stroke-opacity:1" style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:16.2278px;font-family:Montserrat;-inkscape-font-specification:'Montserrat Bold';text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:2.3;stroke-opacity:1"
x="75.235199" x="85.100029"
y="106.55447">XR Forge</tspan></text></g></svg> y="91.992111">XR Forge</tspan></text></g></svg>

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 16 KiB