Custom Libraries
From OpenSimulator
(→Textures) |
(→Getting Assets into the Right Formats) |
||
(11 intermediate revisions by 4 users not shown) | |||
Line 1: | Line 1: | ||
− | |||
{{Quicklinks}} | {{Quicklinks}} | ||
<br /> | <br /> | ||
− | |||
− | |||
− | |||
− | + | = Library Overview = | |
− | + | ||
− | + | ||
+ | The library in OpenSimulator is described in the file <tt>bin/inventory/Libraries.xml</tt>. That file defines a number of other libraries, each of which have a file for folders and a file for items. The files described in <tt>Libraries.xml</tt> are read each time the server starts. (Note, however, that the viewer caches library information, so you may need to clear your cache viewer side to see changes you've made.) | ||
− | + | If you want to add assets to the Library on your OpenSimulator server, there are two fundamental steps. The first is generating the assets, the second is generating these inventory XML files. The assets have information about the objects themselves; the geometry of prims, the image of textures, the text of notecards, etc. The inventory items are entries in the folders that allow users to actually access the assets. | |
− | + | ||
If you want ready-to-import libraries, you could check out the [[Free Assets]] section... | If you want ready-to-import libraries, you could check out the [[Free Assets]] section... | ||
− | == | + | = Generating Assets = |
− | === | + | |
− | + | The server also is able to bulk import asset sets. These asset sets are described in the file <tt>bin/assets/AssetSets.xml</tt>. Each asset set has its own XML file describing the assets, coupled with another file that has the actual asset data (image or text data). You can define new asset sets and add the XML file describing them to <tt>AssetSets.xml</tt>. When Robust (in grid mode) or OpenSimulator (in standalone mode) starts up, it looks at the AssetSets.xml file. It figures out which of the assets in the asset sets don't exist in its asset store, and adds them. | |
− | * | + | |
− | + | == Generating Assets With the Viewer == | |
− | * | + | |
+ | The easiest way to generate assets is just to create them in-world, or upload them with the viewer. If you do this, there is no need to muck about with asset sets. | ||
+ | |||
+ | == In Bulk == | ||
+ | |||
+ | However, if you want to create assets in bulk, then you need to define your own asset set. Look at some of the asset sets defined in <tt>AssetSets.xml</tt>, to get a sense for what the an asset set XML file looks like. | ||
+ | |||
+ | Practically speaking, at the moment, you can only import lsl scripts, notecards, and textures this way. There are two steps. First, you must get the assets into the right format. Second, you must generate the XML files for the assets. | ||
+ | |||
+ | === Getting Assets into the Right Formats === | ||
+ | |||
+ | Notecards and lsl files are plain text files. | ||
+ | |||
+ | Images have a size that is a power of two on each side. That is, both the width and height must be one of 16, 32, 64, 128, 256, 512, or 1024. They must also be in be in JPEG2000 .j2k format. You can use the [http://www.openjpeg.org/ OpenJPEG] utilities to convert from other formats to JPEG2000. Note that version 1.3 of the OpenJPEG libraries does not support greyscale images or transparency. It will fail to work on greyscale images, and will remove any alpha channel from transparent images. As of revision 824 in the OpenJPEG svn archives, it also supports transparency, and may read PNG files directly, and (at least with PNG files) handles greyscale images. It's worth compiling a more recent version [http://code.google.com/p/openjpeg/source/checkout from the svn archives], at least until a version of OpenJPEG is officially released that supports these features. | ||
+ | |||
+ | Convert a single image file to OpenJPEG with | ||
+ | |||
+ | <tt>image_to_j2k -i ''inputfile'' -o ''outputbase''.j2k -r 100,30,10</tt> | ||
+ | |||
+ | A note on compression: the values after "-r" are the compression ratios to store in the OpenJPEG file. If you upload an image with the viewer (at least with Imprudence), the list of compression ratios used is 1920,480,120,30,10. Bigger numbers mean more compression, and hence blurrier images. The advantage of storing multiple levels of compression is that when the (small amount) of data for a high level of compression is downloaded, at least that much can be rendered. This is why when you watch a texture load, it starts blurry, and (at least if you're lucky) eventually becomes sharp. A compression level of 1 is lossless... and also uses a lot of space in the asset store, and a lot of bandwidth when it's sent down. A smallest compression of 10 should be good for most purposes. However, for things like sculpt maps (or small, precise textures), you may want to add a compression ratio of 1 to the end of the arguments. | ||
+ | |||
+ | Two scripts exist, a [[jp2 batch converter|Linux script]] or [[Jp2 batch converter for windows|Windows script]] in perl. They use ImageMagick and image_to_j2k to resize image files to the appropriate size and convert them to j2k files (although named with an extension of .jp2, since that's what the viewer expects for some perverse reason). However, if you want to use this, be aware of some caveats: | ||
+ | |||
+ | * The script ''deletes'' your original image file. | ||
+ | * The script ''does no error checking''. This means that if for whatever reason (and there can be several!) image_to_j2k file fails, the script will delete your image file ''anyway''. This could make you sad if, like a certain stupid wiki page editor, you didn't make backup copies first. | ||
+ | * The script makes j2k files with compression ratios 20,10,1. While this is great for image files that are bundled with the viewer, it's not good for server-side image files. That lossless compression means gratuitous use of server asset store space and bandwidth. | ||
+ | |||
+ | Maybe one day there will be an updated script.... | ||
+ | |||
+ | <source lang="python"> | ||
+ | import os | ||
+ | import subprocess | ||
+ | |||
+ | # Info: https://github.com/uclouvain/openjpeg | ||
+ | # Never compress tile textures. | ||
+ | |||
+ | # Set paths | ||
+ | input_dir = '512png' | ||
+ | output_dir = '512_converted_j2k' | ||
+ | |||
+ | # Create output directory if it doesn't exist | ||
+ | if not os.path.exists(output_dir): | ||
+ | os.makedirs(output_dir) | ||
+ | |||
+ | # Function to convert PNG to J2K | ||
+ | def convert_png_to_j2k(input_path, output_path): | ||
+ | command = [ | ||
+ | 'opj_compress', | ||
+ | '-i', input_path, | ||
+ | '-o', output_path, | ||
+ | '-r', '1' # Lossless compression | ||
+ | ] | ||
+ | try: | ||
+ | subprocess.run(command, check=True) | ||
+ | print(f"Successfully converted: {input_path} -> {output_path}") | ||
+ | except subprocess.CalledProcessError as e: | ||
+ | print(f"Error converting {input_path}: {e}") | ||
+ | |||
+ | # Traverse the input directory and convert PNG files | ||
+ | for filename in os.listdir(input_dir): | ||
+ | if filename.lower().endswith('.png'): | ||
+ | input_path = os.path.join(input_dir, filename) | ||
+ | output_filename = os.path.splitext(filename)[0] + '.j2k' | ||
+ | output_path = os.path.join(output_dir, output_filename) | ||
+ | convert_png_to_j2k(input_path, output_path) | ||
+ | |||
+ | print("Conversion completed.") | ||
+ | </source> | ||
+ | |||
+ | === Creating Asset Set XML Files === | ||
+ | |||
+ | Once you have all your assets in the right format, you need to create the AssetSet XML file. Ultimately, you're going to want to make a new subdirectory underneath <tt>bin/assets</tt>. For example, let's suppose you're making a new asset set called <tt>MyAwesomeAssetSet</tt>. You'll need to create, in that directory, a file <tt>MyAwesomeAssetSet.xml</tt>. You can use the python script [[updatelibrary.py]] to generate this file. It will also generate the inventory XML files needed. This script is documented [[#Generating_Inventory_and_Asset_Files|below]]. | ||
+ | |||
+ | |||
+ | == Terrains == | ||
+ | === Create a terrain heightmap === | ||
+ | |||
+ | * [[Using L3DT|Creating custom terrains with L3DT]] | ||
+ | |||
+ | |||
+ | = Generating Inventory Items = | ||
+ | |||
+ | The folders that show up in the library are defined in <tt>bin/inventory/Libraries.xml</tt>. That xml file has an entry for each library; each library is defined by two XML files. One describes the folders in the library, (i.e. subfolders underneath a top folder that shows up underneath OpenSimulator Library), and the other describes the items in those folders. What you probably want to do is create a new library. Make a subdirectory for that library. Generate the two XML files describing the folders and items in that library. Add an entry to <tt>Libraries.xml</tt> giving the name of the two XML files for your library. You're done! Except that it's very painful, because it will involve copying and generating a lot of UUIDs. As such, except for the most trivial of libraries, you don't want to do this by hand. | ||
+ | |||
+ | == Generating Inventory and Asset Files == | ||
+ | |||
+ | The python script <tt>[[updatelibrary.py]]</tt> is able to generate asset and inventory XML files at the same time. Basically, you maintain a tree of assets in an asset set directory, and run this script to sync the XML files with your tree. (Note that at the moment it can only ''add'' assets; it will not delete them if you remove them frmo yoru tree.) It is able to add both assets in your asset set, ''and'' pre-existing assets already in your grid's asset store (for which you don't have to add something to an asset set.) | ||
+ | |||
+ | In order to use it, you must: | ||
+ | * Create an asset set directory | ||
+ | * Arrange all of your asset files (text files for scripts and notecards, properly sized j2k files for textures) in directories underneath that asset set directory that will correspond to the folders and subfolders underneath your top-level library directory. | ||
+ | * Decide on the name of your library, and on a short version (without spaces) that will be used for filenames. | ||
+ | |||
+ | For example, if you're making a library that will show up as a folder under the OpenSimulator Library folder as "My Awesome Library", you could build it inside the MyAwesomeAssetSet directory. Underneath that directory, make subdirectories named Textures, Clothing, Objects, and so forth. In the Textures directory, put your properlly formatted j2k files. Right now, the script only handles textures (.j2k), notecards (.txt), and scripts (.lsl). | ||
+ | |||
+ | In addition, in each directory you can put a file named "addassets.lis". You can list assets that are already in your grid's asset store here to be added to the library at the folder corresponding to the directory of each adassets.lis file. ''Any'' type of asset may be added to the library this way. This file is a series of lines, each of which follows the format | ||
+ | |||
+ | asset_uuid asset_type inventory_type name of object in inventory | ||
+ | |||
+ | A viewer such as Imprudence is able to tell you the asset_uuid of anything in your inventory; you can just copy this to the addassets.lis file. asset_type is a number, depending on the type of asset: | ||
+ | |||
+ | {|border="2" cellpadding="2" cellspacing="2" | ||
+ | !Type of Object | ||
+ | !asset_type | ||
+ | ! | ||
+ | !Type of Object | ||
+ | !asset_type | ||
+ | |- | ||
+ | |Texture | ||
+ | |0 | ||
+ | | | ||
+ | |Notecard | ||
+ | |7 | ||
+ | |- | ||
+ | |Sound | ||
+ | |1 | ||
+ | | | ||
+ | |LSL Text | ||
+ | |10 | ||
+ | |- | ||
+ | |Calling Card | ||
+ | |2 | ||
+ | | | ||
+ | |LSL Bytecode | ||
+ | |11 | ||
+ | |- | ||
+ | |Landmark | ||
+ | |3 | ||
+ | | | ||
+ | |Bodypart | ||
+ | |13 | ||
+ | |- | ||
+ | |Clothing | ||
+ | |5 | ||
+ | | | ||
+ | |Animation | ||
+ | |20 | ||
+ | |- | ||
+ | |Primitive | ||
+ | |6 | ||
+ | | | ||
+ | |Gesture | ||
+ | |21 | ||
+ | |} | ||
− | |||
− | + | inventory_type is another number, again depending on the type of inventory. (Inventory type and asset type are not the same thing, although they're often closely related. Inventory type is what tells the viewer which sort of icon to show next to the inventory item's name.) | |
− | + | {| border="2" cellpadding="2" cellspacing="2" | |
+ | !Type of Object | ||
+ | !inventory_type | ||
+ | ! | ||
+ | !Type of Object | ||
+ | !inventory_type | ||
+ | |- | ||
+ | |Texture | ||
+ | |0 | ||
+ | | | ||
+ | |Root Category | ||
+ | |9 | ||
+ | |- | ||
+ | |Sound | ||
+ | |1 | ||
+ | | | ||
+ | |LSL Script | ||
+ | |10 | ||
+ | |- | ||
+ | |Calling Card | ||
+ | |2 | ||
+ | | | ||
+ | |Snapshot | ||
+ | |15 | ||
+ | |- | ||
+ | |Landmark | ||
+ | |3 | ||
+ | | | ||
+ | |Attachment | ||
+ | |17 | ||
+ | |- | ||
+ | |Object | ||
+ | |6 | ||
+ | | | ||
+ | |Wearable | ||
+ | |18 | ||
+ | |- | ||
+ | |Notecard | ||
+ | |7 | ||
+ | | | ||
+ | |Animation | ||
+ | |19 | ||
+ | |- | ||
+ | |Category | ||
+ | |8 | ||
+ | | | ||
+ | |Gesture | ||
+ | |20 | ||
+ | |} | ||
− | + | (There is also the "None" inventory type, which has number -1. Body Parts and Clothing are both wearables.) | |
− | + | ||
− | + | Run the script. (Run it with <tt>--help</tt> to get usage instructions.) It will go through the entire directory tree where you have put your asset files. It will read the Asset Set, Inventory Folder, and Inventory Item XML files that already exist. It will then add entries as necessary to those files for any new .j2k, .txt, or .lsl files in your directory tree, as well as for any new entries in any addassets.lis file. | |
+ | When the script is complete, you need only to edit <tt>inventory/Libraries.xml</tt> and <tt>assets/AssetSets.xml</tt> to tell it about your new asset set and your new library. Next time you start up OpenSimulator or (in grid mode) Robust, it should import your assets and add your entries to the library. Again, remember that the viewer caches the library list, so you may need to clear your cache on the viewer in order to get the current version of the library. | ||
+ | == Caution == | ||
− | + | Library data is currently pulled from the region itself rather than the inventory service So in grid mode, library inventory xml files must exist not only on the Robust server, but also on each and every individual region OpenSimulator server. | |
− | + | This needs to change in the future so that library data is loaded directly into the inventory service from the robust server and distributed to individual grids on request (chiefly on user login). This may place more strain on the inventory service, though in the case of inventory it may be more feasible to do a long lived cache of the data on the simulator-side. | |
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | Contact Rob Knop (rknop on freenode, usually hanging out in opensim-dev; Prospero Frobozz on many grids) with any questions about <tt>[[updatelibrary.py]]</tt>. | |
− | + | ||
− | + | ||
− | + |
Latest revision as of 05:54, 17 June 2024
Languages: |
English Deutsch |
Contents |
[edit] Library Overview
The library in OpenSimulator is described in the file bin/inventory/Libraries.xml. That file defines a number of other libraries, each of which have a file for folders and a file for items. The files described in Libraries.xml are read each time the server starts. (Note, however, that the viewer caches library information, so you may need to clear your cache viewer side to see changes you've made.)
If you want to add assets to the Library on your OpenSimulator server, there are two fundamental steps. The first is generating the assets, the second is generating these inventory XML files. The assets have information about the objects themselves; the geometry of prims, the image of textures, the text of notecards, etc. The inventory items are entries in the folders that allow users to actually access the assets.
If you want ready-to-import libraries, you could check out the Free Assets section...
[edit] Generating Assets
The server also is able to bulk import asset sets. These asset sets are described in the file bin/assets/AssetSets.xml. Each asset set has its own XML file describing the assets, coupled with another file that has the actual asset data (image or text data). You can define new asset sets and add the XML file describing them to AssetSets.xml. When Robust (in grid mode) or OpenSimulator (in standalone mode) starts up, it looks at the AssetSets.xml file. It figures out which of the assets in the asset sets don't exist in its asset store, and adds them.
[edit] Generating Assets With the Viewer
The easiest way to generate assets is just to create them in-world, or upload them with the viewer. If you do this, there is no need to muck about with asset sets.
[edit] In Bulk
However, if you want to create assets in bulk, then you need to define your own asset set. Look at some of the asset sets defined in AssetSets.xml, to get a sense for what the an asset set XML file looks like.
Practically speaking, at the moment, you can only import lsl scripts, notecards, and textures this way. There are two steps. First, you must get the assets into the right format. Second, you must generate the XML files for the assets.
[edit] Getting Assets into the Right Formats
Notecards and lsl files are plain text files.
Images have a size that is a power of two on each side. That is, both the width and height must be one of 16, 32, 64, 128, 256, 512, or 1024. They must also be in be in JPEG2000 .j2k format. You can use the OpenJPEG utilities to convert from other formats to JPEG2000. Note that version 1.3 of the OpenJPEG libraries does not support greyscale images or transparency. It will fail to work on greyscale images, and will remove any alpha channel from transparent images. As of revision 824 in the OpenJPEG svn archives, it also supports transparency, and may read PNG files directly, and (at least with PNG files) handles greyscale images. It's worth compiling a more recent version from the svn archives, at least until a version of OpenJPEG is officially released that supports these features.
Convert a single image file to OpenJPEG with
image_to_j2k -i inputfile -o outputbase.j2k -r 100,30,10
A note on compression: the values after "-r" are the compression ratios to store in the OpenJPEG file. If you upload an image with the viewer (at least with Imprudence), the list of compression ratios used is 1920,480,120,30,10. Bigger numbers mean more compression, and hence blurrier images. The advantage of storing multiple levels of compression is that when the (small amount) of data for a high level of compression is downloaded, at least that much can be rendered. This is why when you watch a texture load, it starts blurry, and (at least if you're lucky) eventually becomes sharp. A compression level of 1 is lossless... and also uses a lot of space in the asset store, and a lot of bandwidth when it's sent down. A smallest compression of 10 should be good for most purposes. However, for things like sculpt maps (or small, precise textures), you may want to add a compression ratio of 1 to the end of the arguments.
Two scripts exist, a Linux script or Windows script in perl. They use ImageMagick and image_to_j2k to resize image files to the appropriate size and convert them to j2k files (although named with an extension of .jp2, since that's what the viewer expects for some perverse reason). However, if you want to use this, be aware of some caveats:
- The script deletes your original image file.
- The script does no error checking. This means that if for whatever reason (and there can be several!) image_to_j2k file fails, the script will delete your image file anyway. This could make you sad if, like a certain stupid wiki page editor, you didn't make backup copies first.
- The script makes j2k files with compression ratios 20,10,1. While this is great for image files that are bundled with the viewer, it's not good for server-side image files. That lossless compression means gratuitous use of server asset store space and bandwidth.
Maybe one day there will be an updated script....
import os import subprocess # Info: https://github.com/uclouvain/openjpeg # Never compress tile textures. # Set paths input_dir = '512png' output_dir = '512_converted_j2k' # Create output directory if it doesn't exist if not os.path.exists(output_dir): os.makedirs(output_dir) # Function to convert PNG to J2K def convert_png_to_j2k(input_path, output_path): command = [ 'opj_compress', '-i', input_path, '-o', output_path, '-r', '1' # Lossless compression ] try: subprocess.run(command, check=True) print(f"Successfully converted: {input_path} -> {output_path}") except subprocess.CalledProcessError as e: print(f"Error converting {input_path}: {e}") # Traverse the input directory and convert PNG files for filename in os.listdir(input_dir): if filename.lower().endswith('.png'): input_path = os.path.join(input_dir, filename) output_filename = os.path.splitext(filename)[0] + '.j2k' output_path = os.path.join(output_dir, output_filename) convert_png_to_j2k(input_path, output_path) print("Conversion completed.")
[edit] Creating Asset Set XML Files
Once you have all your assets in the right format, you need to create the AssetSet XML file. Ultimately, you're going to want to make a new subdirectory underneath bin/assets. For example, let's suppose you're making a new asset set called MyAwesomeAssetSet. You'll need to create, in that directory, a file MyAwesomeAssetSet.xml. You can use the python script updatelibrary.py to generate this file. It will also generate the inventory XML files needed. This script is documented below.
[edit] Terrains
[edit] Create a terrain heightmap
[edit] Generating Inventory Items
The folders that show up in the library are defined in bin/inventory/Libraries.xml. That xml file has an entry for each library; each library is defined by two XML files. One describes the folders in the library, (i.e. subfolders underneath a top folder that shows up underneath OpenSimulator Library), and the other describes the items in those folders. What you probably want to do is create a new library. Make a subdirectory for that library. Generate the two XML files describing the folders and items in that library. Add an entry to Libraries.xml giving the name of the two XML files for your library. You're done! Except that it's very painful, because it will involve copying and generating a lot of UUIDs. As such, except for the most trivial of libraries, you don't want to do this by hand.
[edit] Generating Inventory and Asset Files
The python script updatelibrary.py is able to generate asset and inventory XML files at the same time. Basically, you maintain a tree of assets in an asset set directory, and run this script to sync the XML files with your tree. (Note that at the moment it can only add assets; it will not delete them if you remove them frmo yoru tree.) It is able to add both assets in your asset set, and pre-existing assets already in your grid's asset store (for which you don't have to add something to an asset set.)
In order to use it, you must:
- Create an asset set directory
- Arrange all of your asset files (text files for scripts and notecards, properly sized j2k files for textures) in directories underneath that asset set directory that will correspond to the folders and subfolders underneath your top-level library directory.
- Decide on the name of your library, and on a short version (without spaces) that will be used for filenames.
For example, if you're making a library that will show up as a folder under the OpenSimulator Library folder as "My Awesome Library", you could build it inside the MyAwesomeAssetSet directory. Underneath that directory, make subdirectories named Textures, Clothing, Objects, and so forth. In the Textures directory, put your properlly formatted j2k files. Right now, the script only handles textures (.j2k), notecards (.txt), and scripts (.lsl).
In addition, in each directory you can put a file named "addassets.lis". You can list assets that are already in your grid's asset store here to be added to the library at the folder corresponding to the directory of each adassets.lis file. Any type of asset may be added to the library this way. This file is a series of lines, each of which follows the format
asset_uuid asset_type inventory_type name of object in inventory
A viewer such as Imprudence is able to tell you the asset_uuid of anything in your inventory; you can just copy this to the addassets.lis file. asset_type is a number, depending on the type of asset:
Type of Object | asset_type | Type of Object | asset_type | |
---|---|---|---|---|
Texture | 0 | Notecard | 7 | |
Sound | 1 | LSL Text | 10 | |
Calling Card | 2 | LSL Bytecode | 11 | |
Landmark | 3 | Bodypart | 13 | |
Clothing | 5 | Animation | 20 | |
Primitive | 6 | Gesture | 21 |
inventory_type is another number, again depending on the type of inventory. (Inventory type and asset type are not the same thing, although they're often closely related. Inventory type is what tells the viewer which sort of icon to show next to the inventory item's name.)
Type of Object | inventory_type | Type of Object | inventory_type | |
---|---|---|---|---|
Texture | 0 | Root Category | 9 | |
Sound | 1 | LSL Script | 10 | |
Calling Card | 2 | Snapshot | 15 | |
Landmark | 3 | Attachment | 17 | |
Object | 6 | Wearable | 18 | |
Notecard | 7 | Animation | 19 | |
Category | 8 | Gesture | 20 |
(There is also the "None" inventory type, which has number -1. Body Parts and Clothing are both wearables.)
Run the script. (Run it with --help to get usage instructions.) It will go through the entire directory tree where you have put your asset files. It will read the Asset Set, Inventory Folder, and Inventory Item XML files that already exist. It will then add entries as necessary to those files for any new .j2k, .txt, or .lsl files in your directory tree, as well as for any new entries in any addassets.lis file.
When the script is complete, you need only to edit inventory/Libraries.xml and assets/AssetSets.xml to tell it about your new asset set and your new library. Next time you start up OpenSimulator or (in grid mode) Robust, it should import your assets and add your entries to the library. Again, remember that the viewer caches the library list, so you may need to clear your cache on the viewer in order to get the current version of the library.
[edit] Caution
Library data is currently pulled from the region itself rather than the inventory service So in grid mode, library inventory xml files must exist not only on the Robust server, but also on each and every individual region OpenSimulator server.
This needs to change in the future so that library data is loaded directly into the inventory service from the robust server and distributed to individual grids on request (chiefly on user login). This may place more strain on the inventory service, though in the case of inventory it may be more feasible to do a long lived cache of the data on the simulator-side.
Contact Rob Knop (rknop on freenode, usually hanging out in opensim-dev; Prospero Frobozz on many grids) with any questions about updatelibrary.py.