Welcome to the Second Life Forums Archive

These forums are CLOSED. Please visit the new forums HERE

How to dynamically create objects and apply textures to them

Josh Jeffries
Registered User
Join date: 8 Oct 2006
Posts: 3
10-21-2006 13:50
Hello,

I would like to do the following in LSL. Is it possible?

1) Import about 150 images from my computer
2) Write a script that iterates over these images, and
3) Creates an object in SL, then
4) Applies the image as a texture to the object, then
5) Resizes the object so that the x and z correspond proportionally to the original image bounds, and the z is set to 0.020

The point is to turn those 150 objects into virtual "paintings", ready for hanging in a virtual art gallery.

Is this possible to do with LSL? If not 100% possible with LSL, what about if I wrote my own code in C#, for example, to create the LSL statements that would then perform the resizing tasks since I can get the image dimensions from .NET image methods?

Someone told me you cannot create objects with LSL, you can only rez them. If this is the case, will I have to manually create those 150 objects, or at a minimum, make 150 copies?

Thanks!
Josh
Llauren Mandelbrot
Twenty-Four Weeks Old.
Join date: 26 Apr 2006
Posts: 665
Instant Galleria?
10-21-2006 14:16
From: Josh Jeffries
Hello,
Hello.
From: Josh Jeffries
I would like to do the following in LSL. Is it possible?

1) Import about 150 images from my computer
2) Write a script that iterates over these images, and
3) Creates an object in SL, then
4) Applies the image as a texture to the object, then
5) Resizes the object so that the x and z correspond proportionally to the original image bounds, and the z is set to 0.020

The point is to turn those 150 objects into virtual "paintings", ready for hanging in a virtual art gallery.

Is this possible to do with LSL?
Only items 3, 4 & 5 can be done from LSL, although you could write an LSL script that chats output that can be pasted into a script editor window.
From: Josh Jeffries
If not 100% possible with LSL, what about if I wrote my own code in C#, for example, to create the LSL statements that would then perform the resizing tasks since I can get the image dimensions from .NET image methods?
You can easily automate the script-writing this way. You still need to apply the scripts manually, however.
From: Josh Jeffries
Someone told me you cannot create objects with LSL, you can only rez them.
More-or-less true.
From: Josh Jeffries
If this is the case, will I have to manually create those 150 objects, or at a minimum, make 150 copies?
Not at all. You can easily have one object rez 150 copies of another object from the rezor`s own inventory.
From: Josh Jeffries
Thanks!
Josh
You`re welcome.

May I suggest something?

Use your C# programming skills to automaticaly create a single script that will react to an inventory drop, read the name of the texture that was dropped into the inventory, resize the object based on the name of the object, and delete itself and the texture. The object should automaticaly retexture itself on all sides when you drop the texture onto it. If this is not what you want, you may have to have the script retexture the other sides, say to resemble a picture-frame and canvas-back.

Alternatively, drop all 150 textures into a rezzor, and have the rezzor iterate over the textures, creatiing a new object from inventory and using llWhisper() to tell the new object what texture to use and what size to assume. The script in the new object would apply the texture by UUID, resize the prim, perform any other required adjustments, and delete itself.
Josh Jeffries
Registered User
Join date: 8 Oct 2006
Posts: 3
Thanks
10-21-2006 17:52
Thanks Llauren,

I already have all the images uploaded. I did that manually. Are you saying that I can automate the process of creating an object? I have to read up and find out what you mean by "rezzor" too :-)

So far, I added all the textures to an object and was able to iterate over the inventory and print out the name of each in the on touch event, but I only could find functions for rezzing an object, but not for creating a new one.

From: Llauren Mandelbrot
Hello.Only items 3, 4 & 5 can be done from LSL, although you could write an LSL script that chats output that can be pasted into a script editor window.You can easily automate the script-writing this way. You still need to apply the scripts manually, however.More-or-less true.Not at all. You can easily have one object rez 150 copies of another object from the rezor`s own inventory.You`re welcome.

May I suggest something?

Use your C# programming skills to automaticaly create a single script that will react to an inventory drop, read the name of the texture that was dropped into the inventory, resize the object based on the name of the object, and delete itself and the texture. The object should automaticaly retexture itself on all sides when you drop the texture onto it. If this is not what you want, you may have to have the script retexture the other sides, say to resemble a picture-frame and canvas-back.

Alternatively, drop all 150 textures into a rezzor, and have the rezzor iterate over the textures, creatiing a new object from inventory and using llWhisper() to tell the new object what texture to use and what size to assume. The script in the new object would apply the texture by UUID, resize the prim, perform any other required adjustments, and delete itself.
DoteDote Edison
Thinks Too Much
Join date: 6 Jun 2004
Posts: 790
10-21-2006 22:53
You could do it all within LSL, if I understand correctly. Here's how I would do it:

First, make a pim which contains all the textures for a particular gallery wihtin its contents. Name each texture using the following format: "texture name, x aspect, y aspect". For example; American Flag, 2, 1.

Now, add a script to that prim which will receive an integer via the on_rez() event parameter (the integer will be sent by a main rezzing object described below). The integer will be used to query data about an image in this cube's contents. The texture 'name' is a comma-separated-value string... so the script will use llCSV2List() to extract the three data elements; name, x aspect, y aspect. The script will then llSetTexture(), llSetObjectName(), and llSetScale(). This script will execute each time the cube is rezzed and could be made to delete itself afterwards (when it's no longer useful).

Next, create a second cube which will become the main rezzer. and drop a copy of the cube described above, into the contents of this rezzer. Create a script for the rezzer which runs a simple counter that counts up to the total number of pictures needed for your gallery. Make it so that each touch of the rezzer will rez a copy of the cube described above, send the counter integer as the llRezObject() parameter, and increae the counter by 1.

Each time you click the rezzer prim, a copy of the cube inside will be rezzed... the script in the new cube will execute and apply to appropriate texture and dimensions.
Newgate Ludd
Out of Chesse Error
Join date: 8 Apr 2005
Posts: 2,103
10-22-2006 02:14
My two pennies worth. Its really jsut an extension of DoteDote's suggestions but...

Create a blank canvas object.
In it have a script that will listen to a specific channel, specified in the on_rez call.
Put this into a server object containing the textures.
Have the server object rez a new canvas object for each picture, passing it a channel number.
Then the server tells the canvas the UUID of the texture to display and sizes etc.
The canvas script can then selfdelete leaving you with just the picture.


From: DoteDote Edison
First, make a notecard with one line per texture formatted as UUID, x, y:

The textures in the notecard can still be specified by name, you can get the UUID from the name using llGetInventoryKey, Which makes it far more readable.
Josh Jeffries
Registered User
Join date: 8 Oct 2006
Posts: 3
Thanks
10-22-2006 05:17
Thanks again for these great tips. I'm starting to get it, I think :-)

Although, I'm not quite sure I really understand how much can be automated and how much needs to be done by hand.

I know moving them into place will need to be done by hand. That is fine.

But, I'm not sure what else needs to be done by hand? Do I need to write a script that specifies the dimensions of each object ahead of time, or is there a way to get that from using function calls? If not, I could probably just write a C# program to generate the script, no big deal.


Next thing is that I'm not sure what all of the rezzing and objects stuff means. I currently have a cube that I added 100 images to in its contents. I also added a script. All the script does is, on touch, iterate over the inventory of TEXTUREs and llSay the name of the texture.

Will I need to manually create 150 cubes and add them to the same contents inventory, or will I be able to generate 150 instances using script alone?

Sorry if I'm not understand yet, but LSL is very new and I'm trying to work my head around the capabilities and limitations to figure out how much I need to do in C# and how much will be manual / LSL.

Thanks!
Josh




From: Newgate Ludd
My two pennies worth. Its really jsut an extension of DoteDote's suggestions but...

Create a blank canvas object.
In it have a script that will listen to a specific channel, specified in the on_rez call.
Put this into a server object containing the textures.
Have the server object rez a new canvas object for each picture, passing it a channel number.
Then the server tells the canvas the UUID of the texture to display and sizes etc.
The canvas script can then selfdelete leaving you with just the picture.



The textures in the notecard can still be specified by name, you can get the UUID from the name using llGetInventoryKey, Which makes it far more readable.
Newgate Ludd
Out of Chesse Error
Join date: 8 Apr 2005
Posts: 2,103
10-22-2006 06:38
From: Josh Jeffries
Thanks again for these great tips. I'm starting to get it, I think :-)

Although, I'm not quite sure I really understand how much can be automated and how much needs to be done by hand.

I know moving them into place will need to be done by hand. That is fine.


Well Depends exactly how you want to position them. You pass a relative offset to the rezzed canvas which specifies its position as part of the llRezObject call.

From: Josh Jeffries
But, I'm not sure what else needs to be done by hand? Do I need to write a script that specifies the dimensions of each object ahead of time, or is there a way to get that from using function calls? If not, I could probably just write a C# program to generate the script, no big deal.


I dont think you can read the size of a texture using LSL, anyone care to correct me?
I'd use a notecard with the names rather than UUID's as suggested by DoteDote earlier.
The problem you will have is that SL sizes are specified in metre's not pixels, but you should be able to use a magic number to convert. I's suggest adding it as a parameter to the description note card so that you can change it with out having to recompile the script.

From: Josh Jeffries
Next thing is that I'm not sure what all of the rezzing and objects stuff means.


A script can rez an object from its inventory using llRezObject
this allows you to position, rotate and even apply a velocity to the rezzed object.
Additionally you can pass one extra parameter, param which is an integer. My suggestion is that you use this to specify a channel number over which the server/controller and the canvas communicate.

The following code fragment may help explain:

CODE

integer Channel = 0;

default
{
state_entry()
{
Channel = (integer)llFrand(999)+100;
}

touch_start(integer total_number)
{
// The following parameters would be supplied by the notecard
// but for this example...
string texturename = "Picture1";
string size = "<1.0,2.0,0.1>";
key textureid = llGetInventoryKey(texturename);
// Rez One Meter above the control prim.
llRezObject("Canvas", llGetPos() + (<0.0, 0.0, 1.0> * llGetRot()),<0,0,0>, llGetRot(), Channel);
llSleep(1); // Pause to let it rez
llSay(Channel,"SET_TEXTURE=" + (string)textureId);
llSleep(1); // Pause to let it update
llSay(Channel,"SET_SIZE=" + size);
}
}


Obviously you will need a corresponding script in the Canvas to handle the received messages. Something along the following lines should work:

CODE

integer Channel = 0;
integer primface = ALL_SIDES; // the side you want to display the texture on

default
{
state_entry()
{
}

on_rez(integer number)
{
Channel = number;
llListen(Channel,"","","");
}

listen(integer channel, string name, key id, string message)
{
list ldata = llParseString2List(message, ["="], [""]);
string command = llList2String(ldata,0);
string value = llList2String(ldata,1);
if("SET_TEXTURE" == command)
{
llSetTexture(value, primface);
}
else if("SET_SIZE" == command)
{
vector v = llList2Vector(ldata,1);
llSetScale(v);
}
}
}


From: Josh Jeffries
I currently have a cube that I added 100 images to in its contents. I also added a script. All the script does is, on touch, iterate over the inventory of TEXTUREs and llSay the name of the texture.

Well you can use that to generate the first pass notecard, you will then just need to add the sizes to it. Just cut and paste the chat output into a notecard.


From: Josh Jeffries
Will I need to manually create 150 cubes and add them to the same contents inventory, or will I be able to generate 150 instances using script alone?

You only need to make one canvas prim and then rez it 150 times via the script. Be aware that this could run into the grey goo fence though, so use a sleep call to slow it down.
I'd suggest using bidirectional / full duplex comms to ensure that what your server/controller have sent has been received. It adds to script complexity a little but will make it far easier to debug and to ensure that all parameters have been received and set.


Hope some if not all of that is useful to you.
Llauren Mandelbrot
Twenty-Four Weeks Old.
Join date: 26 Apr 2006
Posts: 665
10-23-2006 08:45
I`m sorry I took so long to get back to you, but I`ve been AFK for a while. :)

DoteDote Edison`s original suggestion [which I didn`t read because he changed it before I could read it] sounds to me like your best bet, in your particular situation. You could easily write a C# program to create the notecard, and use the clipboard to copy the text from your C# program into the notecard.

As noted, you only need to create the painting prim once. As they failed to note, however, once you finish the painting prim, you must add it to the inventory of the rezzor prim. You will then have the rezzor prim use llRezObject() to create 150 copies of the painting prim automatically.

I`d like to suggest a modification to Newgate`s method. Put the notecard in the painting object, before you put the painting object into the rezzor object. Use the touch_start() event to pace the operation, so you don`t hit the Grey Goo fence. Pass the counter number as the start-up parameter in llRezObject(), and in the script in the painting object, use the start parameter to look up a single line in the notecard, and use the results of that look-up to configure the painting object. Then have the painting script delete the contents of the painting object`s inventory, being sure to delete the script last.

After thinking on this a bit longer, I further suggest that you drop the textures into the painting object, not the rezzor object. With everything needed for every painting in the painting object, you need no communications channel from the rezzor object to the painting object beyond what the llRezObject() call already provides. Since you already have an object with the textures in it, use that for the painting object, and move the script you now have into a different object to rez copies of the painting object from.

Newgate: you are correct: You cannot read the size of a texture with LSL. Even if you could, since the texture is resized to a power of two by Second Life when you upload it, you would [probably] not get the original size. Also, Josh specified originally that the sizes would be "proportional" to the original picture sizes, so he apparently was already intending to use a scaling factor. Placing that in the notecard, however, sounds feasable.

Ok, time for recipie:
  1. Painting object:
    1. All textures
    2. Notecard:
      1. Texture name, width, height

    3. Script:
      1. on_rez(integer whichPainting) event:
        1. llNoteCardLine(whichPainting);

      2. dataserver() event:
        1. parse the notecard line into Name, X, and Y, proably with ldata = llCSV2List();




        2. key textureid = llGetInventoryKey(texturename);
        3. llSetScale(llList2Vector(ldata,1));
        4. llSetTexture(value, primface);
        5. delete all textures from inventory;
        6. delete notecard from inventory;
        7. delete self from inventory;



  2. rezor object
    1. Painting object

    2. script:
      1. default

        {

        state_entry()

        {

        integer Painting = 0;

        }

        touch_start(integer total_number)

        {
        // Rez One Meter above the control prim.

        llRezObject("Canvas", llGetPos() + (<0.0, 0.0, 1.0> * llGetRot()),<0,0,0>, llGetRot(),
        Painting);

        }

        }



Newgate Ludd
Out of Chesse Error
Join date: 8 Apr 2005
Posts: 2,103
10-24-2006 04:14
From: Llauren Mandelbrot
I`d like to suggest a modification to Newgate`s method. Put the notecard in the painting object, before you put the painting object into the rezzor object. Use the touch_start() event to pace the operation, so you don`t hit the Grey Goo fence. Pass the counter number as the start-up parameter in llRezObject(), and in the script in the painting object, use the start parameter to look up a single line in the notecard, and use the results of that look-up to configure the painting object. Then have the painting script delete the contents of the painting object`s inventory, being sure to delete the script last.


The draw back to having the textures etc in the painting object is updating complexity.
By only having the data and textures in the rezzer you only need to modify the 'primary' object not the canvas/painting object.

The idea of using touch to iterate is good but for 150 will be labourious. But does allow you to manually position the paintings as you rezz them.

From: Llauren Mandelbrot
After thinking on this a bit longer, I further suggest that you drop the textures into the painting object, not the rezzor object. With everything needed for every painting in the painting object, you need no communications channel from the rezzor object to the painting object beyond what the llRezObject() call already provides. Since you already have an object with the textures in it, use that for the painting object, and move the script you now have into a different object to rez copies of the painting object from.


Wouldn't this is highly inefficient in terms of resource usage as you will be ineffect creating every texture 150 times and deleting it 149 times? Still inefficient in my mind.
The additional resource usage of a listen is minor compared with creating and deleteing 150 unused keys.

One point that hasnt been stated, and its a BIG point, the textures dont need to be in the rezzer prim at all. The rezzer would just contain the notecard. The notecard would contain the UUID's as per DoteDote's original suggestion. The actual textures could all remain 'safely' in your inventory and as long as you didnt delete them no one else would ever get hold of them apart from by buying your pictures.

Combining the two ideas, using UUID's in the note card and putting this into the picture/canvas with the intelligent script would be the most efficient method.
Llauren Mandelbrot
Twenty-Four Weeks Old.
Join date: 26 Apr 2006
Posts: 665
Optimizing for Minimal Operator Involvement?
10-24-2006 07:45
I think you miss something.
From: Newgate Ludd
The draw back to having the textures etc in the painting object is updating complexity.
As I understand it, there is zero updating complexity, as this is strictly a one-off operation, with no intent to repeat.
From: Newgate Ludd
By only having the data and textures in the rezzer you only need to modify the 'primary' object not the canvas/painting object.
Agreed, but irrelavent for a one-off project.
From: Newgate Ludd
The idea of using touch to iterate is good but for 150 will be labourious. But does allow you to manually position the paintings as you rezz them.
Agreed and agreed.
From: Newgate Ludd
Wouldn't this is highly inefficient in terms of resource usage as you will be ineffect creating every texture 150 times and deleting it 149 times? Still inefficient in my mind.
Agreed, but I didn`t think it mattered for a one-off project. For a one-off, the operator`s resources are more important, IMHO. Besides, if you reread what I wrote, I didn`t suggest deleting the texture 149 times. :) I suggested deleting it 150 times. :D
From: Newgate Ludd
The additional resource usage of a listen is minor compared with creating and deleteing 150 unused keys.
Agreed, but again, I was assuming this to be a one-off project, and trying to minimize operator involvement. His C# program can create a notecard with name and size info, but the UUID cannot be determined there; that has to wait for the LSL phase. Still, what I suggested, copying and deleting 150 images 150 times, is very bad programming practice. I hereby withdraw my suggestion for having all 150 images in the painting object.
From: Newgate Ludd
One point that hasnt been stated, and its a BIG point, the textures dont need to be in the rezzer prim at all. The rezzer would just contain the notecard. The notecard would contain the UUID's as per DoteDote's original suggestion. The actual textures could all remain 'safely' in your inventory and as long as you didnt delete them no one else would ever get hold of them apart from by buying your pictures.
Agreed 100%, except that the script needs to get the UUID from somewhere, and the C# program cannot generate it. In the interest of reducing operator workload, it is required to have the images somewhere where LSL can access them by name.
From: Newgate Ludd
Combining the two ideas, using UUID's in the note card and putting this into the picture/canvas with the intelligent script would be the most efficient method.
..if --and only if-- we ignore the innefficiency of manualy pasting 150 UUIDs into a notecard that is only going to be used once. :)
Llauren Mandelbrot
Twenty-Four Weeks Old.
Join date: 26 Apr 2006
Posts: 665
Follow-up idea!
10-24-2006 07:52
Follow-up idea:

Copy the images into a rezzor object that does not rez anything, but instead spits the name and UUID to open chat.

Copy the results into the C# program.

Use the C# program to create a notecard as follows:
  1. Use the name to look up the file, and extract the image size from the file.
  2. Write the UUID and size to STDOUT [or some other appropriate location].
  3. Copy the resulting text into a notecard.
  4. Place the notecard into the painting object.
  5. Proceed as outlined in my previous posts, as modified by Newgate.

Newgate Ludd
Out of Chesse Error
Join date: 8 Apr 2005
Posts: 2,103
10-24-2006 08:05
From: Llauren Mandelbrot
I think you miss something.
As I understand it, there is zero updating complexity, as this is strictly a one-off operation, with no intent to repeat.

Well it is never stated that this will be a strictly one off operation . And what if at some point we want to make a second (different) Gallery ? :)

From: Llauren Mandelbrot
..if --and only if-- we ignore the innefficiency of manualy pasting 150 UUIDs into a notecard that is only going to be used once.


Well As stated in post 1 there is already a script in the prim with the ability to say the names, editing this to say the UUID's instead and then cut and paste into the notecard.

I agree with you Llauren to certain point, but I've always designed software to be as reusable as possible.

Another solution, use PSP to remake all the textures the same size...... (just kidding)
Llauren Mandelbrot
Twenty-Four Weeks Old.
Join date: 26 Apr 2006
Posts: 665
Misunderstandings? [My fault?]
10-24-2006 08:28
From: Newgate Ludd
Well it is never stated that this will be a strictly one off operation . And what if at some point we want to make a second (different) Gallery ? :)
Point taken....and in that case, he would already have a recipie for another operator-effort-minimal one-off. :)

Um, I note that in my previous post I was confusing two uses of "one-off". The recipie is a re-usable recipie for creating one-off painting rezors. Once the paintings were created, the rezor wound no longer be needed to re-create those paintings, but the recipie could be re-used to create another one-off batch-creator of paingings. Indeed, it is very possible that the parts created for one one-off could be used for the creation of a second one-off, and I see no difficulty in using them so.

The recipie is not one-off, but it is for the creation of one-off rezzors. My balance was for minimal operator involvement in the entire process.
From: Newgate Ludd
Well As stated in post 1 there is already a script in the prim with the ability to say the names, editing this to say the UUID's instead and then cut and paste into the notecard.
I saw that. In post #3. :) Indeed, I was thinking of this when I suggested my "Follow-up Idea". I suppose I should have said so, though.
From: Newgate Ludd
I agree with you Llauren to certain point, but I've always designed software to be as reusable as possible.
Point taken. I still feel that our combined recipie is the best one for operator-minimal effort, even for a second Gallery.

...or third, or fourth, or... :D
From: Newgate Ludd
Another solution, use PSP to remake all the textures the same size...... (just kidding)
Probably a good idea, anyway, as SL doesn`t always resize to power-of-two well, except the original poster has already uploaded them.:o
Newgate Ludd
Out of Chesse Error
Join date: 8 Apr 2005
Posts: 2,103
10-25-2006 01:57
From: Llauren Mandelbrot
Point taken. I still feel that our combined recipie is the best one for operator-minimal effort, even for a second Gallery.

...or third, or fourth, or...


So do I :) , sorry may be I should have stated that, hang on I did....

From: Newgate Ludd
Combining the two ideas, using UUID's in the note card and putting this into the picture/canvas with the intelligent script would be the most efficient method.