Welcome to the Second Life Forums Archive

These forums are CLOSED. Please visit the new forums HERE

Library: 3D Model Importer (Beta and Disclaimer)

Daten Thielt
Registered User
Join date: 1 Dec 2006
Posts: 104
02-20-2007 17:51
Hey for some reaons whenever i try to look at any of the screenshots of this or when i try to get the macro im getting a permmisions denied page
Kanoni Basevi
Registered User
Join date: 8 Mar 2007
Posts: 1
Thanks
03-11-2007 06:22
Thanks for making this available. I prefer OBJ import over the SL editor for Blender.

However, I wonder, did anyone tried a sim optimized version of this code? I get even cubes make out of two tris instead of one flatend cube per face. But the SL editor has quares available. The same accounts for round shapes, these are getting constructed out of many many tries instead of one sphere object.

Anyhow, this alraedy helps a lot in importing some of the details we are planning. Thanks again. Maybe I'll find some time in the weeks to come and look into prim optimization. If there is anything outthere, let me know please.

Chears,
Kan

P.S.: For anybody new or returning to SL (like I am), adding an object means drag and drop it from inventory to the targets objects content property. Might be trivial for exerience SL builders, but took me some time to figure it out :)
foxjack Allen
Registered User
Join date: 25 Apr 2007
Posts: 1
Notification
04-26-2007 08:13
It would appear that the link to the Text Macro does not work anymore.
Onionboy Oppewall
Registered User
Join date: 14 May 2007
Posts: 1
Importing prims with color
05-22-2007 07:01
I've been working with the importer and love it. One question, when I import my objects, they always have the default texture. I suppose this is because there are no texture parameters define in the file exported by Blender.

Is there a way to manually edit the texture parameters to specify a blank texture and a color?

<textures params="">
</textures>

Thanks.
Pants Munro
Registered User
Join date: 31 May 2007
Posts: 1
05-31-2007 14:35
From: Slobeck Tuncsik
there's currently a 1.0 release of a script called "primBuilder" for Blender - a very powerful, if a bit odd, open source 3D package... blender can be got from blender.org and the primbuilder comes from sourceforge. I've been messing with it, and it's definitely a nicer way to make things. There's an in-world importer that is being given away by the author. You just build, save, open the file in a text editor, and cut and paste it into a note card in SL, then drop the note card on the importer, and the object rezzes. -- not to detract from this effort. I'd rather be able to just model and import obj's, but each poly as a prim? That's gonna be murder on the sims. I hope this can develop though, good luck!


Hi im new but have a lot of experience in design. Could tell me the step by step process of what u did above to import models.

Cheers Pants
Spider Mycron
Registered User
Join date: 25 Oct 2006
Posts: 133
06-05-2007 15:57
Hi every one..
after all of this :
could some one please write on steps what shall i do ? i saw new update over and i'm ready to try this i'm using 3dmax 8

i would appreciate this please

i need to know in steps with the update

Best regards
_____________________
Spider Mycron
Life Heart Beat
Philian Canning
Registered User
Join date: 16 May 2007
Posts: 1
07-14-2007 19:08
OK, I've tried everything to get this thing to work with no luck. I've followed every set of instructions that I've found (all of them are different). I keep getting the same error: "object missing from database." Could someone who has gotten this thing to work, please, break it down for me?

Thnx.
riso Myoo
Registered User
Join date: 18 Oct 2007
Posts: 2
GrAtZ ^_^
10-18-2007 06:54
i just would like to congratulate!

I'm new at SL but i am a 3D modeller and can't w8 to try this script and to see how does it worx.. go on man.. go on :)


rISO
Italy
Ollj Oh
Registered User
Join date: 28 Aug 2007
Posts: 522
01-26-2008 02:41
Hmmm. didn`t I once convert half life maps like de.dust into .obj files?
Day Oh
Registered User
Join date: 3 Feb 2007
Posts: 1,257
01-26-2008 03:42
It's great work :D
I tried to make something like this to import Quake maps, lol
I ended up giving up and just modifying this one a bit.

The key differences are, it can read multiple notecards to use as many vertices and faces as you want, and the faces can have many vertices, the script will separate a face into triangles.

Here is the main script:

From: someone

integer DATA_FUNC_FACE = 1;
integer DATA_FUNC_VERTEX = 2;

string note_name;
integer note_card;
integer note_line;

list verts_needed;
integer verts_total;
list verts_got;
integer verts_returned;

integer data_func;
key data_query;

float setting_scale;
integer setting_verbose;

list queue_channels;
list queue_listens;
list queue_datas;




request_vertex(integer vertex)
{
vertex = vertex - 1;
integer card = llFloor(vertex / 1024);
integer line = vertex % 1024;
data_func = DATA_FUNC_VERTEX;
data_query = llGetNotecardLine(note_name + ".vertices." + (string)card, line);
if(setting_verbose >= 3)
llOwnerSay("me requested vertex " + (string)vertex + " from note " + (string)card + " line " + (string)line);
}



default
{
on_rez(integer param)
{
llResetScript();
}

state_entry()
{
llListen(0, "", llGetOwner(), "";);
llOwnerSay("/me is ready for command.";);
}

listen(integer channel, string name, key id, string message)
{
if(channel)
{
integer index = llListFindList(queue_channels, [channel]);
if(index != -1)
{
llListenRemove(llList2Integer(queue_listens, index));
queue_listens = llDeleteSubList(queue_listens, index, index);
queue_channels = llDeleteSubList(queue_channels, index, index);
llRegionSay(channel, llList2String(queue_datas, index));
queue_datas = llDeleteSubList(queue_datas, index, index);
}
}
else
{
message = llStringTrim(message, STRING_TRIM);
integer divide = llSubStringIndex(message, " ";);
if(divide == -1)
divide = 0;
string command = llToLower(llGetSubString(message, 0, divide - 1));
string parameter = llGetSubString(message, divide + 1, -1);
if(command == "build";)
{
note_name = parameter;
data_func = DATA_FUNC_FACE;
data_query = llGetNotecardLine(note_name + ".faces." + (string)(note_card = 0), note_line = 0);
}
else if(command == "scale";)
{
setting_scale = (float)parameter;
if(setting_scale == 0.0)
setting_scale = 1.0;
llOwnerSay("Scale factor set to " + (string)setting_scale + ".";);
}
else if(command == "verbose";)
{
setting_verbose = (integer)parameter;
llOwnerSay("Verbose set to " + (string)setting_verbose + ".";);
}
}
}

dataserver(key queryid, string data)
{
if(queryid != data_query)
return;
if(data_func == DATA_FUNC_FACE)
{
if(data != EOF)
{
if(llStringLength(data))
{
// This notecard line tells us what verts the face needs
verts_needed = [];
list tokens = llParseString2List(data, ["f", " "], []);
integer divide;
string buffer;
integer a;
verts_total = llGetListLength(tokens);
for(a = 0; a < verts_total; a++)
{
buffer = llList2String(tokens, a);
divide = llSubStringIndex(buffer, "/";);
if(divide != -1)
{
buffer = llGetSubString(buffer, 0, divide - 1);
}
verts_needed += [(integer)buffer];
}
if(setting_verbose >= 2)
llOwnerSay("/me loaded a face requiring vertices " + llList2CSV(verts_needed));
// Now I need to load vertices before making triangles
verts_got = [];
verts_returned = 0;
request_vertex(llList2Integer(verts_needed, 0));
// sets off the loading of the rest of the vertices
}
// next...
// MOVED

}
else
{
// end of note, move on or finish
note_card++;
if(llGetInventoryType(note_name + ".faces." + (string)note_card) == INVENTORY_NOTECARD)
{
data_func = DATA_FUNC_FACE;
data_query = llGetNotecardLine(note_name + ".faces." + (string)note_card, note_line = 0);
}
else
{
llOwnerSay("Done!";);
}
}
}
else if(data_func == DATA_FUNC_VERTEX)
{
list tokens = llParseString2List(data, ["v", " "], []);
verts_got = (verts_got = []) + verts_got + [<;(float)llList2String(tokens, 0), (float)llList2String(tokens, 1), (float)llList2String(tokens, 2)> * setting_scale];
if(setting_verbose >= 3)
llOwnerSay("Contents of vertex buffer now: " + llList2CSV(verts_got));

// another dataserver event plz
if(++verts_returned < verts_total)
{
// get the next one
request_vertex(llList2Integer(verts_needed, verts_returned));
}
else
{
// got all vertices for this face... queue triangles!
integer random;
integer a;
for(a = 2; a < verts_total; a++)
{
random = (integer)(-llFrand(65535)) - 65535;
queue_channels = (queue_channels = []) + queue_channels + [random];
queue_listens = (queue_listens = []) + queue_listens + [llListen(random, "", "", "on_rez";)];
queue_datas = (queue_datas = []) + queue_datas + [(string)llList2Vector(verts_got, 0) + (string)llList2Vector(verts_got, a - 1) + (string)llList2Vector(verts_got, a)];
llRezObject("triangle", llGetPos(), ZERO_VECTOR, ZERO_ROTATION, random);
if(setting_verbose)
llOwnerSay("Triangle: " + (string)llList2Vector(verts_got, 0) + (string)llList2Vector(verts_got, a - 1) + (string)llList2Vector(verts_got, a));
}

// next face
data_func = DATA_FUNC_FACE;
data_query = llGetNotecardLine(note_name + ".faces." + (string)note_card, ++note_line);
}
}
}
}


Drop that script in a prim, then create a second prim and drop in this script:

From: someone

//================================================== ==========================
// Prim Face Creation Code
//
// For Use with Jeffrey Gomez's OBJ Model Importer For Second Life
//
// For Usage Instructions, Rights, and Other Relevant Info, See Other Script.
//
// Major Variables:
//
float thickness = 0.01; //Thickness of each face.
string sim = ""; //Desired sim for script; Initialization only!
//================================================== ==========================

warpPos( vector destpos)
{ //R&D by Keknehv Psaltery, 05/25/2006
//with a little pokeing by Strife, and a bit more
//some more munging by Talarus Luan
//Final cleanup by Keknehv Psaltery
// Compute the number of jumps necessary
integer jumps = (integer)(llVecDist(destpos, llGetPos()) / 10.0) + 1;
// Try and avoid stack/heap collisions
if (jumps > 100 )
jumps = 100; // 1km should be plenty
list rules = [ PRIM_POSITION, destpos ]; //The start for the rules list
integer count = 1;
while ( ( count = count << 1 ) < jumps)
rules = (rules=[]) + rules + rules; //should tighten memory use.
llSetPrimitiveParams( rules + llList2List( rules, (count - jumps) << 1, count) );
if ( llVecDist( llGetPos(), destpos ) > .001 ) //Failsafe
while ( --jumps )
llSetPos( destpos );
}


vector Proj(vector u, vector v) {
// Return the projection of u onto v.
// Special Thanks to Xylor Baysklef for jogging my memory on this
return ( (u * v) / (v * v) ) * v;
}

vector crossProduct(vector one, vector two)
{
//================================================== ======================
// This returns a Cross Product of two vectors. If you have no idea what
// that means, look it up.
//
// Note no actual call to this function exists. This is because it exists
// in LSL's modulus (%) command. It's simply from old code.
//================================================== ======================

return <;(one.y * two.z) - (one.z * two.y),(one.z * two.x) - (one.x * two.z),(one.x * two.y) - (one.y * two.x)>;
}


integer g_listener = FALSE; //Just a measly listener. Ho hum. Don't hurt me.
default
{
on_rez(integer param)
{
if(param)
{
integer param = llGetStartParameter();
g_listener = llListen(param,"","","";);
llSay(param, "on_rez";);

sim = llGetRegionName();
}
}
//================================================== ======================
// Most of the hard stuff happens here. I'll explain it each time we run
// across something, so if you don't understand it, that's my fault.
// This data is called by a listener event, the listener of which is
// promptly removed to save server load and keep faces from listening to
// every command. Trust me, I had that happen. It wasn't pretty.
//================================================== ======================

listen(integer chan, string name, key id, string msg)
{
llListenRemove(g_listener);

//================================================== ==================
//
// Phase One: Get Our Vectors
//
// Nothing unduly interesting here. We just take our string and get
// the vector values we want from them.
//
//================================================== ==================

vector vector1 = (vector)llGetSubString(msg,0,llSubStringIndex(msg, ">";));
msg = llDeleteSubString(msg,0,llSubStringIndex(msg,">";));
vector vector2 = (vector)llGetSubString(msg,0,llSubStringIndex(msg, ">";));
msg = llDeleteSubString(msg,0,llSubStringIndex(msg,">";));
vector vector3 = (vector)llGetSubString(msg,0,llSubStringIndex(msg, ">";));

//================================================== ==================
//
// Phase Two: Edge Detection Calls
//
// Now comes the fun stuff. Since we've been passed our largest edge,
// vector one and two, which prevents our script from causing problems
// later, we now need to find which edge is closer to the odd vector
// out (vector3). We do this because the smallest edge, or closest
// vector, to it will make for a handy little edge on a 3D parallelo-
// gram that we have to construct to find out points in this script
//
// If you're not lost yet, don't worry. There are plenty of places
// along the way to fall off the bus. =]
//
//================================================== ==================

float edge1 = llVecMag(vector1 - vector3);
float edge2 = llVecMag(vector2 - vector3);

vector vect = (vector1 + vector2 + vector3) / 3;
//Failsafe initialization

//This constructs the last edge of our parallelogram.
if(edge1 < edge2)
{
vect = vector3 - vector1;
vect += vector2;
}
else
{
vect = vector3 - vector2;
vect += vector1;
}

//================================================== ==================
//
// Phase Three: Lots of Stuff Happens
//
// No better way to put it. Now, the reason we created that 3D
// parallelogram (remember? That shape in math class that everyone
// just called a crooked box) is because it's a pretty handy shape,
// since the corners all have the same angle and, more importantly,
// we can use one to find out the center of mass of the face!
//
// And now, for those of you that flunked math class. Center of Mass
// is just a really long way of saying "Average Them, Stupid."
//
// Unfortunately, even with that center, we're not done. Once we have
// it, we have to find the Primitive Center that Second Life uses.
// That's a little harder. To do that, we borrow something from
// Linear Algebra: Projections. Basically, we take a point in 3D and
// project it onto a line, find where it is on the line, and go from
// there.
//
// Said number will give us the value we can use to find Second Life's
// center, or a fair approximation (see below). We then go from the
// center of vectors one and two to the distance we just found.
//
// And that, ladies and gentlemen, is our center! *applause*
//
//================================================== ==================

vector duoCenter = (vector1 + vector2) / 2;
vector Center = (vector1 + vector2 + vector3 + vect) / 4;

vector dot = vector1 + Proj(Center - vector1,vector2 - vector1);
vector Center2 = duoCenter + (Center - dot);


//================================================== ==================
//
// Phase Four: The Reason You Flunked Physics Class
//
// This is rather hard to explain. Simply put, though, a Cross Product
// does something very, very very very very ... helpful for us here:
//
// It gives us a place to go.
//
// Okay. What it does for us here isn't quite that simple. Actually,
// what it does for us is take our values, and extends an axis
// perpendicular to them. That might not sound like much here, but
// that does something really important, because the Linden sponsored
// command, llAxes2Rot, turns axes into a rotation! Thanks, Lindens!
//
// </shameless plug>
//
// Anyway, after we do that, we finally go to where we want to go
// and rotate to the proper rotation.
//
//================================================== ==================

vector fwd = ((vector2 - duoCenter) / llVecMag(vector2 - duoCenter)) % ((Center - dot) / llVecMag(Center - dot));
rotation rot = llAxes2Rot(fwd,((Center - dot) / llVecMag(Center - dot)) % fwd,(Center - dot) / llVecMag(Center - dot));

//vector pos = llGetPos();
//integer i = 0;
//while(pos + Center2 != llGetPos())
//{
//i += 1;
//if(llGetRegionName() != sim) llDie();
//llSetPos(pos + Center2);
//if(i >= 100) llDie();
//}
//llSetPos(pos + Center2);
warpPos(llGetPos() + Center2);
llSetRot(rot);


//================================================== ==================
//
// Phase Final: "Tri" Means "Three"
//
// This took me a little while to figure out. What we do now is take
// all of our data from above, and put a new dot on that imaginary
// parallelogram, right on the edge between vector3 and the last
// corner that we made earlier. We do this to toy with the Top Shear
// value of a prim to emulate the final vertex. To do that, we just
// see how far vector3 is from that new dot, and convert it to
// something Second Life understands.
//
// Once that's done, we get to resize and reshape our prim, and we're
// done!
//
// ... but wait, now all I have is a triangle!
//
// Ah, but that's the beauty of it. See, all those little triangles
// will now take the shape of whatever your original object was!
//
//================================================== ==================

vector transpose = Center2 + (Center - dot);
float shear = llVecMag(transpose - vector3) / llVecMag(vector2 - vector1);
if(llVecMag(vector1 - dot) > llVecMag(vector2 - dot)) shear *= -1;

vector size = <thickness,2 * llVecMag(vector2 - duoCenter),2 * llVecMag(dot - Center)>;

llSetPrimitiveParams([PRIM_SIZE,size]);
llSetPrimitiveParams([PRIM_TYPE,PRIM_TYPE_BOX,00,<0.000000, 1.000000, 0.000000>,0.000000,<0.000000, 0.000000, 0.000000>,<1.000000, 0.000000, 0.000000>,<0.000000, shear, 0.000000>]);
integer j;
integer tot = llGetInventoryNumber(INVENTORY_SCRIPT);
for(j = 0; j < tot; j++)
{
if(llGetInventoryName(INVENTORY_SCRIPT,j) != llGetScriptName())
llRemoveInventory(llGetInventoryName(INVENTORY_SCRIPT,j));
}
llRemoveInventory(llGetScriptName());
}
}


Take that second prim and put it in the inventory of the first prim, along with the first script.
Then you'll need to create notecards with your faces and vertices...

Come up with a name for your import, like "house." The notecards need to be named like house.faces.0, house.faces.1, etc, and the notecards for vertices need to be named house.vertices.0, house.vertices.1, and so on...

When creating the notecards for vertices, make sure they each have exactly 1024 vertices, up until the last notecard, it can have fewer.
The notecards for faces, on the other hand, can each have any number of faces.

The faces notecards look like this:

From: someone

f 1/1 2/2 3/3 4/4
f 5/5 6/6 7/7 8/8
f 9/9 10/10 11/11 12/12
f 13/13 14/14 15/15 16/16
f 17/17 18/18 19/19 20/20
. . .


And vertices notecards:

From: someone

v 0.296000 0.192000 0.192000
v 0.256000 0.192000 0.192000
v 0.256000 0.192000 -0.064000
v 0.296000 0.192000 -0.064000
. . .


Personally I used some notepad find/replace to concoct my notecard texts... goto line 1024, cut and paste, repeat...

Once you've got a prim that contains the main script, notecards, and the second scripted prim inside, you're ready... to begin, issue commands in chat:

To set the scale multiplier for building:
From: someone
scale 0.5


To build:
From: someone
build house


Well... here's hoping maybe one person will be able to work it and find it useful :D
_____________________
1 2 3