Rayne@Posted: Mon Aug 30, 2004 1:08 am :
NURBS tutorial

So… my second tutorial, i’m going to explain how nurbs will work, and how to use them to produce a smooth pattern for entities movements..

I really liked the smooth movements of the monorail train, so I just tried to figure how they at ID did this. I’ve found that nurbs are involved.

Probably, you are thinkin “oh yeah, now tell us how NURBS editor (placed in the patches menu) will work, since i tried it and looks a little buggy...” . No, it IS buggy and is not needed to create NURBS. I spent half an hour to clarify that... Damn...


What we are gonna use:

Basically, we use

1) Radiant
2) A basic script

What we are gonna achieve:

Basically, I will create a func mover with a light attached on it, that will follow the path of the NURBS spline we assign to it and create a smooth curved movement hard to achieve with normal scripting.


Let’s start

Fire up radiant, and open or create a famous “testbox” map of 512x512 units(a room to test our stuff). I’ve got dozens of them.


[img=http://img222.echo.cx/img222/5428/gcs00154mi.th.jpg]


Try to mantain it a bit dark, cause since we are adding a moving light the effect will be more clear & cool.

Now, let’s create the heart of our tutorial, the NURBS. Create a small box, I usually use a 2x2 box using grid 8. This will become the “entity” that contain our spline (basically, a simple func_static). Since we don’t want that the player can see it in game, we apply the “nodraw” shader on it. Now, you have a cube like this:


[img=http://img94.echo.cx/img94/3535/gcs0016big2gn.th.jpg]


Ok, now the cool thing: make it func_Static and press SHIFT + O just in the case the origin spot is not in the cube center. NAME IT as you like, i want to name my entity “ spline1 “. With this fresh new func_static, go in the entity menu, and select “Curve...”

This button:


[img=http://img244.echo.cx/img244/1482/button6hx.th.jpg]


A new menu will pop up, and you have two choices... Select NURBS since this tutorial is on them :D

Magically, a blu line will appear from the entity origin, making a short 90° bend.. very smooth, but too small to use it for anything.


[img=http://img245.echo.cx/img245/7776/nurbbig9gi.th.jpg]


NURB EDITING.

Now we got a nurbs, and we want to edit it: if you take a look to your radiant standard menu, you can notice these buttons (if you run radiant on a resolution equal or minor of 1024x768, you have to undock the menu bar in order to reach these fuckin buttons, cause by default they are out of the screen)


[img=http://img259.echo.cx/img259/7503/buttonsmenu7cj.th.jpg]


They are in this order: EDIT, ADD, INS, DEL.

So, with your func_static selected, hit the EDIT button: this will pop up three small blue boxes, that you can actually move around in all the 3 view to tweak your nurbs :-D


[img=http://img249.echo.cx/img249/4042/bluebox9ho.th.jpg]


Buttons are very self explanatory, so you can add points, insert a point between others, delete if you don’t want it etc. So try to create a complex spline that will travel around your room. I’ve done this

[img=http://img258.echo.cx/img258/1696/topbig0fg.th.jpg]


The only thing that is important to understand, is that the FIRST point of your spline IS the start of the actual path: the func_static object is only the entity that carry spline informations (for reference). So i adjusted the start of my spline here

[img=http://img244.echo.cx/img244/6826/startpoint4pb.th.jpg]


Everything that will move following this path, will start its animation from there.

Ok, now save your map. Let’s add some eye-candy stuff: first we need an anchor that actually move around the path. I made a small func_mover box (nodraw shader on it) named “ anchor “ (without quotes) AND PLACED ITS ORIGIN IN THE SAME PLACE of our spline STARTPOINT.


[img=http://img256.echo.cx/img256/5328/anchor2if.th.jpg]


The anchor is ready, so now we are going to add the real gimmick stuff. I’ve added a spotlight with the texture “lights/sentrylight” that point on the opposite wall. I’ve placed it some units over the anchor... I added also a func_emitter with the “lamplight1” particle effect that point in the same direction of the light.

Now, you have to bind the func_emitter and the light to the anchor entity. Simply, add

Key: bind
Value: anchor

In both of the entities. Now i have something like this.


[img=http://img257.echo.cx/img257/3493/figurenorm4os.th.jpg]
[img=http://img247.echo.cx/img247/8069/figurecool6ju.th.jpg]



Last, but non least, we create a simple trigger_once that will call a function present in our script file. Place it around the map, so you can walk through it, and add this keys

Key: call
Value: light_start



Ok, we have finished in the editor, let’s go scripting.



Scripting part:

I assume that you know scripting basics, how to create a script file and where you have to place it. You must have this lines in order to let it work:

Code:
void light_start()

{
        $anchor.time(20);
        $anchor.accelTime(5);
        $anchor.decelTime(5);

        $anchor.startSpline($spline1);

}



void main()

{

     
}



Ok, “Light_start” is the name of the function that we have added in the trigger_once to start the event (remember “call” “light_start”? ) so when you call that function, the engine will performe every action is into is brakets.

$anchor.time accelTime and decelTime are used to set the animation duration of our entity. (Please refer to Rich is bored beautiful tutorials here on DooM3World)


And finally, the important line is

$anchor.startSpline($spline1);

that is the line that actually START up the animation of our anchor entity. Usage is

$name_of_the_entity_to_animate.startSpline($name_of_the_func_static_that_hold_spline_informations);



Now, if you compile and run your map, try to walk in the trigger once, so you have a very well animated moving light.


Conclusions: obviously this tutorial will introduce the basics of NURBS, but you can think about the power of that type of animations... You can do advanced scripted movements for machinery, trains, vehicles and so on, limit is only your imagination.


I add my testmap to give you something to check, simply open with the editor and compile it.

TO STAFF: i will temporary add my map in text format, if you can point me to a good free webspace, I will upload on it :wink:



NURBS_TESTMAP (copy the code and paste it in a text file. Save it with a name like NURBS_TESTMAP.MAP and remember that you need the script also)



Version 2
// entity 0
{
"classname" "worldspawn"
// primitive 0
{
brushDef3
{
( 0 0 -1 -168 ) ( ( 0.015625 0 0 ) ( 0 0.0078125 0 ) ) "textures/base_wall/bluetex1d_ed" 0 0 0
( 0 0 1 -328 ) ( ( 0.015625 0 0 ) ( 0 0.0078125 0 ) ) "textures/base_wall/bluetex1d_ed" 0 0 0
( 0 -1 0 -248 ) ( ( 0.015625 0 0 ) ( 0 0.0078125 0 ) ) "textures/base_wall/bluetex1d_ed" 0 0 0
( 0 1 0 -248 ) ( ( 0.015625 0 0 ) ( 0 0.0078125 0 ) ) "textures/base_wall/bluetex1d_ed" 0 0 0
( -1 0 0 -264 ) ( ( 0.015625 0 0 ) ( 0 0.0078125 0 ) ) "textures/base_wall/bluetex1d_ed" 0 0 0
( 1 0 0 256 ) ( ( 0.015625 0 0 ) ( 0 0.0078125 0 ) ) "textures/base_wall/bluetex1d_ed" 0 0 0
}
}
// primitive 1
{
brushDef3
{
( 0 0 -1 -168 ) ( ( 0.015625 0 0 ) ( 0 0.0078125 0 ) ) "textures/base_wall/bluetex1d_ed" 0 0 0
( 0 0 1 -328 ) ( ( 0.015625 0 0 ) ( 0 0.0078125 0 ) ) "textures/base_wall/bluetex1d_ed" 0 0 0
( 1 0 0 -240 ) ( ( 0.015625 0 0 ) ( 0 0.0078125 0 ) ) "textures/base_wall/bluetex1d_ed" 0 0 0
( 0 1 0 -256 ) ( ( 0.015625 0 0 ) ( 0 0.0078125 0 ) ) "textures/base_wall/bluetex1d_ed" 0 0 0
( -1 0 0 -256 ) ( ( 0.015625 0 0 ) ( 0 0.0078125 0 ) ) "textures/base_wall/bluetex1d_ed" 0 0 0
( 0 -1 0 248 ) ( ( 0.015625 0 0 ) ( 0 0.0078125 0 ) ) "textures/base_wall/bluetex1d_ed" 0 0 0
}
}
// primitive 2
{
brushDef3
{
( 0 0 -1 -168 ) ( ( 0.015625 0 0 ) ( 0 0.0078125 0 ) ) "textures/base_wall/bluetex1d_ed" 0 0 0
( 0 0 1 -328 ) ( ( 0.015625 0 0 ) ( 0 0.0078125 0 ) ) "textures/base_wall/bluetex1d_ed" 0 0 0
( 0 -1 0 -248 ) ( ( 0.015625 0 0 ) ( 0 0.0078125 0 ) ) "textures/base_wall/bluetex1d_ed" 0 0 0
( 1 0 0 -248 ) ( ( 0.015625 0 0 ) ( 0 0.0078125 0 ) ) "textures/base_wall/bluetex1d_ed" 0 0 0
( 0 1 0 -248 ) ( ( 0.015625 0 0 ) ( 0 0.0078125 0 ) ) "textures/base_wall/bluetex1d_ed" 0 0 0
( -1 0 0 240 ) ( ( 0.015625 0 0 ) ( 0 0.0078125 0 ) ) "textures/base_wall/bluetex1d_ed" 0 0 0
}
}
// primitive 3
{
brushDef3
{
( 0 0 -1 -168 ) ( ( 0.015625 0 0 ) ( 0 0.0078125 0 ) ) "textures/base_wall/bluetex1d_ed" 0 0 0
( 0 0 1 -328 ) ( ( 0.015625 0 0 ) ( 0 0.0078125 0 ) ) "textures/base_wall/bluetex1d_ed" 0 0 0
( 0 -1 0 -256 ) ( ( 0.015625 0 0 ) ( 0 0.0078125 0 ) ) "textures/base_wall/bluetex1d_ed" 0 0 0
( 1 0 0 -240 ) ( ( 0.015625 0 0 ) ( 0 0.0078125 0 ) ) "textures/base_wall/bluetex1d_ed" 0 0 0
( -1 0 0 -256 ) ( ( 0.015625 0 0 ) ( 0 0.0078125 0 ) ) "textures/base_wall/bluetex1d_ed" 0 0 0
( 0 1 0 248 ) ( ( 0.015625 0 0 ) ( 0 0.0078125 0 ) ) "textures/base_wall/bluetex1d_ed" 0 0 0
}
}
// primitive 4
{
brushDef3
{
( 0 0 1 -336 ) ( ( 0.0078125 0 0 ) ( 0 0.0078125 0 ) ) "textures/base_floor/a_bluetex1a_02" 0 0 0
( 0 -1 0 -248 ) ( ( 0.0078125 0 0 ) ( 0 0.0078125 0 ) ) "textures/base_floor/a_bluetex1a_02" 0 0 0
( 1 0 0 -240 ) ( ( 0.0078125 0 0 ) ( 0 0.0078125 0 ) ) "textures/base_floor/a_bluetex1a_02" 0 0 0
( 0 1 0 -248 ) ( ( 0.0078125 0 0 ) ( 0 0.0078125 0 ) ) "textures/base_floor/a_bluetex1a_02" 0 0 0
( -1 0 0 -256 ) ( ( 0.0078125 0 0 ) ( 0 0.0078125 0 ) ) "textures/base_floor/a_bluetex1a_02" 0 0 0
( 0 0 -1 328 ) ( ( 0.0078125 0 0 ) ( 0 0.0078125 0 ) ) "textures/base_floor/a_bluetex1a_02" 0 0 0
}
}
// primitive 5
{
brushDef3

{
( 0 0 -1 -176 ) ( ( 0.0078125 0 0 ) ( 0 0.0078125 0 ) ) "textures/base_floor/a_bluetex1a_02" 0 0 0
( 0 -1 0 -248 ) ( ( 0.0078125 0 0 ) ( 0 0.0078125 0 ) ) "textures/base_floor/a_bluetex1a_02" 0 0 0
( 1 0 0 -240 ) ( ( 0.0078125 0 0 ) ( 0 0.0078125 0 ) ) "textures/base_floor/a_bluetex1a_02" 0 0 0
( 0 1 0 -248 ) ( ( 0.0078125 0 0 ) ( 0 0.0078125 0 ) ) "textures/base_floor/a_bluetex1a_02" 0 0 0
( -1 0 0 -256 ) ( ( 0.0078125 0 0 ) ( 0 0.0078125 0 ) ) "textures/base_floor/a_bluetex1a_02" 0 0 0
( 0 0 1 168 ) ( ( 0.0078125 0 0 ) ( 0 0.0078125 0 ) ) "textures/base_floor/a_bluetex1a_02" 0 0 0
}
}
}
// entity 1
{
"classname" "trigger_once"
"name" "trigger_once_1"
"model" "trigger_once_1"
"origin" "-8 104 -152"
"call" "light_start"
// primitive 0
{
brushDef3
{
( 0 0 -1 -16 ) ( ( 0.0625 0 0 ) ( 0 0.0625 0 ) ) "textures/common/trigonce" 0 0 0
( 0 0 1 -24 ) ( ( 0.0625 0 0 ) ( 0 0.0625 0 ) ) "textures/common/trigonce" 0 0 0
( 0 -1 0 -24 ) ( ( 0.0625 0 0 ) ( 0 0.0625 0 ) ) "textures/common/trigonce" 0 0 0
( 1 0 0 -88 ) ( ( 0.0625 0 0 ) ( 0 0.0625 0 ) ) "textures/common/trigonce" 0 0 0
( 0 1 0 -32 ) ( ( 0.0625 0 0 ) ( 0 0.0625 0 ) ) "textures/common/trigonce" 0 0 0
( -1 0 0 -80 ) ( ( 0.0625 0 0 ) ( 0 0.0625 0 ) ) "textures/common/trigonce" 0 0 0
}
}
}
// entity 2
{
"classname" "func_emitter"
"name" "func_emitter_1"
"origin" "-216 96 -56"
"rotation" "-2.38273e-007 1 -2.9473e-009 3.71506e-008 2.94731e-009 1 1 2.38273e-007 -3.71506e-008"
"model" "lamplight1.prt"
"bind" "anchor"
}
// entity 3
{
"classname" "light"
"name" "light_4"
"origin" "-200 96 -58"
"noshadows" "0"
"nospecular" "0"
"nodiffuse" "0"
"falloff" "1.000000"
"texture" "lights/sentrylight"
"_color" "1 1 1"
"light_target" "664 0 0"
"light_up" "0 0 200"
"light_right" "0 -200 0"
"bind" "anchor"
}
// entity 4
{
"classname" "func_mover"
"name" "anchor"
"model" "anchor"
"origin" "-208 96 -64"
// primitive 0
{
brushDef3
{
( 0 0 -1 -4 ) ( ( 0.0625 0 6.25 ) ( 0 0.0625 -19.25 ) ) "textures/common/nodraw" 0 0 0
( 0 0 1 -4 ) ( ( 0.0625 0 6.25 ) ( 0 0.0625 19.25 ) ) "textures/common/nodraw" 0 0 0
( 0 -1 0 -4 ) ( ( 0.0625 0 19.25 ) ( 0 0.0625 0.25 ) ) "textures/common/nodraw" 0 0 0
( 1 0 0 -4 ) ( ( 0.0625 0 6.25 ) ( 0 0.0625 0.25 ) ) "textures/common/nodraw" 0 0 0
( 0 1 0 -4 ) ( ( 0.0625 -0 -19.25 ) ( 0 0.0625 0.25 ) ) "textures/common/nodraw" 0 0 0
( -1 0 0 -4 ) ( ( 0.0625 0 -6.25 ) ( 0 0.0625 0.25 ) ) "textures/common/nodraw" 0 0 0
}
}
}
// entity 5
{
"classname" "func_static"
"name" "spline1"
"model" "spline1"
"origin" "-232 96 -64"
"curve_Nurbs" "17 ( -207.86 96.14 -64 -181.86 100.14 -64 -123.86 104.14 -56 -119.86 224.14 48 132.59 225.91 76.44 212.18 106.36 187.61 158.62 -140.75 61.5 58.12 -33.14 2.93 153.54 130.32 -20.73 -62.45 97.17 -100.67 -209.86 -47.95 -122.65 -76.9 -149.94 -134 109.77 -68.8 -2.32 215.05 -175.38 32.44 82.06 -218.31 -110.95 -208.44 -216.03 -132.78 -231.56 -73.35 -119.32 )"
// primitive 0
{
brushDef3
{
( 0 0 -1 -8 ) ( ( 0.0625 0.0000000062 7.4914264679 ) ( -0.0000000062 0.0625 -20.4911251068 ) ) "textures/common/nodraw" 0 0 0
( 0 0 1 -8 ) ( ( 0.0625 -0.0000000062 7.4914264679 ) ( 0.0000000062 0.0625 20.4911251068 ) ) "textures/common/nodraw" 0 0 0
( -0.0000019074 -1 0 -7.8628382683 ) ( ( 0.0625 -0 20.491109848 ) ( 0 0.0625 0 ) ) "textures/common/nodraw" 0 0 0
( 1 -0.0000019073 0 -8.1420440674 ) ( ( 0.0625 0 7.4914302826 ) ( 0 0.0625 0 ) ) "textures/common/nodraw" 0 0 0
( 0.0000019073 1 0 -8.1371612549 ) ( ( 0.0625 -0 -20.4911193848 ) ( 0 0.0625 0 ) ) "textures/common/nodraw" 0 0 0
( -1 -0.0000019073 0 -7.8579707146 ) ( ( 0.0625 0 -7.4914112091 ) ( 0 0.0625 0 ) ) "textures/common/nodraw" 0 0 0
}
}
}
// entity 6
{
"classname" "info_player_start"
"name" "info_player_start_2"
"origin" "-8 200 -168"
"angle" "270"
}
// entity 7
{
"classname" "light"
"name" "light_3"
"origin" "0 -16 -64"
"noshadows" "0"
"nospecular" "0"
"nodiffuse" "0"
"falloff" "0.000000"
"_color" "0.38 0.38 0.38"
"light_radius" "344 320 180"
}



rich_is_bored@Posted: Mon Aug 30, 2004 1:13 am :
This is very nice Rayne. :)



tequila@Posted: Mon Aug 30, 2004 1:42 am :
now that's a nice tutorial :)



Bruiser@Posted: Mon Aug 30, 2004 2:50 am :
Great tutorial. It's helped me a ton.

I'm sorry that I have to say this. It was beat into me at school by my modeling instructor. Non-Uniform Rational B-Spline.....there's no such thing as a nurb. :P



The Dude@Posted: Mon Aug 30, 2004 4:14 am :
now heres a question..... how the ehll do you bind a ragdoll to that.... i'v be trying for almost an hour with my code...

here's my situation... i binded a ragdoll to the mover.... and when i saw it doing nothing, i did as in the tutorial, bond a light to it.
as soon as i start up the map, the light is flying around, and i havn't even walked into the triger yet..



The Dude@Posted: Mon Aug 30, 2004 4:20 am :
hahaha ok, nm, i didn't have the trigger brush actually a trigger entity, stupid me, i thought i had changed it, duh..... :roll:

haha BUT, when i did that, fixed it.... CRASH... line 109: parsed 1.#INF

EDIT: found out why he wasn't binding... and crash was a stupid thing on my part.



MNeMiC@Posted: Mon Aug 30, 2004 6:52 am :
Way awsome! Can't wait to get home to try this one out!!!



Rayne@Posted: Mon Aug 30, 2004 4:12 pm :
I'm trying to understand how to add my tutorial in official "DooM3world tutorial listing"...


Will be updated automatically by staff, or I 've got to send a pm, or anything? :D



MNeMiC@Posted: Mon Aug 30, 2004 4:55 pm :
Should be automaticly - and yay, I'm home now!



rich_is_bored@Posted: Mon Aug 30, 2004 5:25 pm :
Oh sorry, I guess I could have done that.

* rushes off to add your tutorial to the list *



Techx@Posted: Mon Sep 06, 2004 2:41 am :
Quote:
AND PLACED ITS ORIGIN IN THE SAME PLACE of our spline STARTPOINT.


edit: I got it to work I fixed the problems I had. Nice tutorial!

Thx,
Techx



bb_matt@Posted: Mon Sep 06, 2004 6:15 am :
Great tutorial Rayne - that's something that will come in very useful for a map idea I'm working on !



Techx@Posted: Mon Sep 06, 2004 10:40 pm :
Can anyone tell me how I can change the angle of the func_mover at the different points along the nurbs path?

thanks



Techx@Posted: Tue Sep 07, 2004 3:16 pm :
BUMP! Please see previous post, I have yet to find an answer to this. Any help is appreciated

Edit: Ok to explain the problem I'm having better here is an illustration I did very quickly (sorry I'm not that good with these types of things)

Image


The Red triangle represents my func_mover that is bound to the nurbs path. The green triangle represents the "front" of the mover, so the effect I am getting is shown in illustration "A" and what I want is represented in "B". I want the front of my mover to follow the path, instead of the object just being moved along the path and not angled in the right direction it is moving in.

Thanks if anyone can help figure out what I'm doing wrong.
Techx



Rayne@Posted: Sat Sep 11, 2004 11:37 pm :
Like the train in the game! What you want is achieved by default.. have you tried my testmap? It show a light that will follow the path and change direction accordingly with the nurbs



Techx@Posted: Wed Sep 15, 2004 7:34 pm :
Rayne wrote:
Like the train in the game! What you want is achieved by default.. have you tried my testmap? It show a light that will follow the path and change direction accordingly with the nurbs


Hrm, i'll check your test map later on.. thanks



evilartist@Posted: Fri Oct 01, 2004 11:03 pm :
It doesn't work...period. There is not a single tutorial that I've tried that does NOT work when it involves creating a new script. It's all a load of crap. If you can prove me wrong, then explain each step more thoroughly, because you can't assume someone knows ALL the basics. :x:cry:



Rayne@Posted: Fri Oct 01, 2004 11:56 pm :
evilartist wrote:
It doesn't work...period.



Infact, this thread is full of people that's screaming "that doesn't work, load of crap, bla blabla", or i'm I wrong?

Quote:
There is not a single tutorial that I've tried that does NOT work when it involves creating a new script.


And this don't let you think that probably YOU are missing something?

Quote:
It's all a load of crap.


Please, show US (all Doom3World) some respect, i've spent some time to make this tutorial, and for what I know, everything works ok since other members tried my tut and are happy with the results.

Quote:
If you can prove me wrong, then explain each step more thoroughly,


More than this? oh my god. why the hell I have to add 3000 lines to my tutorial to describe how to bind two objects, when this forum is FULL of tutorials ON THAT. My tut is on splines, remember?

Quote:
because you can't assume someone knows ALL the basics.


No, i think you have to learn a bit more scripting, then you can come back and try this.. You know, it's advanced stuff :twisted: (and please, cut down all these emoticons, this is not a forum for children


Seriously, now I have to calm down... In the meanwhile, if you would post YOUR ACTUAL PROBLEM, instead of posting insults on my work.. I'll help you.



evilartist@Posted: Sat Oct 02, 2004 12:22 am :
Anytime I do a tutorial based on writing scripts, the scripts never work, despite when I follow every direction specifically. My question is: how am I supposed to correctly create a script file that should work when something within its contents is called?

I apologize for the earlier message, but this happens anytime I follow these types of instructions. What is it about the scripts that I create that don't work? I did exactly what you said. Also, I meant that you should be more detailed because I must be missing something, but I just can't figure out what. Is my script file in the wrong directory? First I tried the base/script folder, then I tried just the base folder, but the game does not recognize what it is supposed to "call."

If you don't mind me asking: how much should I know in advance before attempting this tutorial?



rich_is_bored@Posted: Sat Oct 02, 2004 1:30 am :
Bare minimum, read my first scripting tutorial.