vec_for_vertex gap issue

Posted By: Dooley

vec_for_vertex gap issue - 04/18/18 06:46

I am trying to make some nifty tentacled creatures, and I am using individual segments that are held together with the vec_for_vertex command.

It works, however, if I use too many segments, or if there is some more stuff on the level, the segments start to separate, especially near the end of the tentacles, see image...



I have uploaded a sample of my script, if anyone is interested. I am wondering if there is a way to get the segments to apply their movement in the right order so these gaps don't appear, or if this is a limitation of the engine...

Tentacles Zip File

Here is the actual script, if you don't feel like downloading the file...

Quote:
///////////////////////////////
#include <acknex.h>
#include <default.c>
#include <mtlFX.c>
///////////////////////////////

//Tentacle Test
var segments = 50;
var roots = 20;
var speed = 3;

action segment()
{
set(my,PASSABLE);
my.material = mtl_specBump;

my.skill1 = handle(you);
my.skill2 = you.skill2 + 1;

my.scale_x = you.scale_x * .95;
my.scale_y = my.scale_x;
my.scale_z = my.scale_x;

//initialize movement variables
var my_angles;
var my_speed = speed;

//create next segment
if(my.skill2 < segments)
{
ent_create("tentacle.mdl",my.x,segment);
}

//start loop
while(1)
{
you = ptr_for_handle(my.skill1);

//make rotation changes
my_angles += my_speed * time_step;
my.pan = you.pan + 5 * sin(my_angles);
my.tilt = you.tilt + 5 * sin(my_angles);
my.roll = you.roll + 5 * sin(my_angles);
vec_for_vertex(my.x,you,15);

wait(1);
}
}

var pan_mod = 0;
var tilt_mod = 0;
var roll_mod = 0;

action root()
{
random_seed(0);
my.pan = random(360) + pan_mod;
random_seed(0);
my.tilt = random(90) + tilt_mod;
random_seed(0);
my.roll = random(360) + roll_mod;

random_seed(0);
pan_mod += random(45) + 15;
random_seed(0);
tilt_mod += random(45) + 15;
random_seed(0);
roll_mod += random(45) + 15;

set(my,PASSABLE);
my.material = mtl_specBump;
ent_create("tentacle.mdl",my.x,segment);


if(roots > 0)
{
roots -= 1;
ent_create("tentacle.mdl",my.x,root);
}
}

function main()
{
sky_color.red = 0;
sky_color.green = 57;
sky_color.blue = 77; // bright blue sky

d3d_antialias = 9;

level_load("Tentacles.WMB");
wait(2);
}
Posted By: Kartoffel

Re: vec_for_vertex gap issue - 04/18/18 08:57

to me it looks like some of the segments are just not rotated correctly
Posted By: Superku

Re: vec_for_vertex gap issue - 04/18/18 14:07

Could just be a timing issue, a 1 frame delay (child segments are set before their parents are updated, each frame).
Either way, you should re-think and re-code your approach IMO. Use one action and a single loop for the creature and position subsequent segments iteratively/ via skill linking (loop over main segments, like 10 starting tentacles, then move forward via skills, pointers (faster) or handles (suitable for game_save)). No additional while loops in the segments.
Posted By: Dooley

Re: vec_for_vertex gap issue - 04/18/18 15:21

@Superku That sounds good. I think, instead of storing a pointer to the parent segment, maybe having the parent store a pointer to the child.

I am a little unclear on how I could do it without a while loop in the segments. If my reverse approach above does not work, then I will have to think about that more...

@Kartoffel You're right, they appear to be all facing the same direction... hmmmm
Posted By: Dooley

Re: vec_for_vertex gap issue - 04/18/18 19:54

@Superku Thank you!

Controlling all the segments from one loop did the trick! It's a little less versatile now, since I have to have a fixed number of segments per arm, but they all keep together really nicely.

Here's the code I used, in case anyone else wants to use some tentacles...

Quote:

///////////////////////////////
#include <acknex.h>
#include <default.c>
#include <mtlFX.c>
///////////////////////////////

//Tentacle Test
var segments = .01;
var roots = 50;
var speed = 10;
var taper = .85;
var bend = 10;

action segment()
{
set(my,PASSABLE);
my.material = mtl_specBump;

wait(1);

while(my.skill1 > 0)
{
my.scale_x = my.scale_x * taper;
my.skill1 -= 1;
}

my.scale_y = my.scale_x;
my.scale_z = my.scale_x;

}

var pan_mod = 0;
var tilt_mod = 0;
var roll_mod = 0;

action root()
{
ENTITY* ent_seg01;
ENTITY* ent_seg02;
ENTITY* ent_seg03;
ENTITY* ent_seg04;
ENTITY* ent_seg05;
ENTITY* ent_seg06;
ENTITY* ent_seg07;
ENTITY* ent_seg08;
ENTITY* ent_seg09;
ENTITY* ent_seg10;

random_seed(0);
my.pan = random(360) + pan_mod;
random_seed(0);
my.tilt = random(90) + tilt_mod;
random_seed(0);
my.roll = random(360) + roll_mod;

random_seed(0);
pan_mod += random(45) + 45;
random_seed(0);
tilt_mod += random(45) + 90;
random_seed(0);
roll_mod += random(45) + 180;

set(my,PASSABLE);
my.material = mtl_specBump;

if(roots > 0)
{
roots -= 1;
ent_create("tentacle.mdl",my.x,root);

wait(1);
}

ent_seg01 = ent_create("tentacle.mdl",my.x,segment);
ent_seg01.skill1 = 1;
my.skill1 = handle(ent_seg01);
wait(1);

ent_seg02 = ent_create("tentacle.mdl",my.x,segment);
ent_seg02.skill1 = 2;
my.skill2 = handle(ent_seg02);
wait(1);

ent_seg03 = ent_create("tentacle.mdl",my.x,segment);
ent_seg03.skill1 = 3;
my.skill3 = handle(ent_seg03);
wait(1);

ent_seg04 = ent_create("tentacle.mdl",my.x,segment);
ent_seg04.skill1 = 4;
my.skill4 = handle(ent_seg04);
wait(1);

ent_seg05 = ent_create("tentacle.mdl",my.x,segment);
ent_seg05.skill1 = 5;
my.skill5 = handle(ent_seg05);
wait(1);

ent_seg06 = ent_create("tentacle.mdl",my.x,segment);
ent_seg06.skill1 = 6;
my.skill6 = handle(ent_seg06);
wait(1);

ent_seg07 = ent_create("tentacle.mdl",my.x,segment);
ent_seg07.skill1 = 7;
my.skill7 = handle(ent_seg07);
wait(1);

ent_seg08 = ent_create("tentacle.mdl",my.x,segment);
ent_seg08.skill1 = 8;
my.skill8 = handle(ent_seg08);
wait(1);

ent_seg09 = ent_create("tentacle.mdl",my.x,segment);
ent_seg09.skill1 = 9;
my.skill9 = handle(ent_seg09);
wait(1);

ent_seg10 = ent_create("tentacle.mdl",my.x,segment);
ent_seg10.skill1 = 10;
my.skill10 = handle(ent_seg10);
wait(1);

//initialize movement variables
var my_angles;
var my_speed = speed;

random_seed(0);
segments += random(.2) + .01;
wait(-1 * segments);

//begin waving sequence
while(1)
{
//Move Segment 1
//ent_seg01 = ptr_for_handle(my.skill1);

//make rotation changes
my_angles += my_speed * time_step;

ent_seg01.pan = my.pan + bend * sin(my_angles);
ent_seg01.tilt = my.tilt + bend * sin(my_angles);
ent_seg01.roll = my.roll + bend * sin(my_angles);
vec_for_vertex(ent_seg01.x,my,15);

ent_seg02.pan = ent_seg01.pan + bend * sin(my_angles);
ent_seg02.tilt = ent_seg01.tilt + bend * sin(my_angles);
ent_seg02.roll = ent_seg01.roll + bend * sin(my_angles);
vec_for_vertex(ent_seg02.x,ent_seg01,15);

ent_seg03.pan = ent_seg02.pan + bend * sin(my_angles);
ent_seg03.tilt = ent_seg02.tilt + bend * sin(my_angles);
ent_seg03.roll = ent_seg02.roll + bend * sin(my_angles);
vec_for_vertex(ent_seg03.x,ent_seg02,15);

ent_seg04.pan = ent_seg03.pan + bend * sin(my_angles);
ent_seg04.tilt = ent_seg03.tilt + bend * sin(my_angles);
ent_seg04.roll = ent_seg03.roll + bend * sin(my_angles);
vec_for_vertex(ent_seg04.x,ent_seg03,15);

ent_seg05.pan = ent_seg04.pan + bend * sin(my_angles);
ent_seg05.tilt = ent_seg04.tilt + bend * sin(my_angles);
ent_seg05.roll = ent_seg04.roll + bend * sin(my_angles);
vec_for_vertex(ent_seg05.x,ent_seg04,15);

ent_seg06.pan = ent_seg05.pan + bend * sin(my_angles);
ent_seg06.tilt = ent_seg05.tilt + bend * sin(my_angles);
ent_seg06.roll = ent_seg05.roll + bend * sin(my_angles);
vec_for_vertex(ent_seg06.x,ent_seg05,15);

ent_seg07.pan = ent_seg06.pan + bend * sin(my_angles);
ent_seg07.tilt = ent_seg06.tilt + bend * sin(my_angles);
ent_seg07.roll = ent_seg06.roll + bend * sin(my_angles);
vec_for_vertex(ent_seg07.x,ent_seg06,15);

ent_seg08.pan = ent_seg07.pan + bend * sin(my_angles);
ent_seg08.tilt = ent_seg07.tilt + bend * sin(my_angles);
ent_seg08.roll = ent_seg07.roll + bend * sin(my_angles);
vec_for_vertex(ent_seg08.x,ent_seg07,15);

ent_seg09.pan = ent_seg08.pan + bend * sin(my_angles);
ent_seg09.tilt = ent_seg08.tilt + bend * sin(my_angles);
ent_seg09.roll = ent_seg08.roll + bend * sin(my_angles);
vec_for_vertex(ent_seg09.x,ent_seg08,15);

ent_seg10.pan = ent_seg09.pan + bend * sin(my_angles);
ent_seg10.tilt = ent_seg09.tilt + bend * sin(my_angles);
ent_seg10.roll = ent_seg09.roll + bend * sin(my_angles);
vec_for_vertex(ent_seg10.x,ent_seg09,15);

wait(1);
}
}

function main()
{
sky_color.red = 0;
sky_color.green = 57;
sky_color.blue = 77; // bright blue sky

d3d_antialias = 9;

level_load("Tentacles.WMB");
wait(2);
}
Posted By: Superku

Re: vec_for_vertex gap issue - 04/18/18 20:55

I just gave it a try the way I had it in mind earlier, try this:

Code:
///////////////////////////////
#include <acknex.h>
#include <default.c>
#include <mtlFX.c>
///////////////////////////////

//Tentacle Test
var segments = 50;
var roots = 20;
var speed = 3;

#define nextSegment skill10

void segmentsCreate(ENTITY* eParent, int numSegments)
{
	ENTITY* eNewSegment = ent_create("tentacle.mdl",eParent.x,NULL);
	set(eNewSegment,PASSABLE);
	eNewSegment.material = mtl_specBump;
	eNewSegment.scale_x = eParent.scale_x * .95;
	eNewSegment.scale_y = eNewSegment.scale_x;
	eNewSegment.scale_z = eNewSegment.scale_x;
	eParent.nextSegment = eNewSegment;
	if(numSegments > 0) segmentsCreate(eNewSegment, numSegments-1);
}

action creature()
{
	set(my,INVISIBLE | UNLIT);
	int i;
	for(i = 0; i < roots; i++)
	{
		you = ent_create("tentacle.mdl",my.x,NULL);
		you.material = mtl_specBump;
		your.pan = random(360);
		your.tilt = random(360);
		your.roll = random(360);
		segmentsCreate(you,segments);
		my.skill[19+i] = you;  // my.skill[19+0] is the same as my.skill20
	}
	var mainAngle = 0;
	while(1)
	{
		mainAngle += speed*time_step;
		for(i = 0; i < roots; i++)
		{
			ENTITY* eRoot = my.skill[19+i];
			eRoot.pan += time_step;
			eRoot.tilt += time_step;
			eRoot.roll += time_step;
			
			ENTITY* eSegment = eRoot.nextSegment;
			ENTITY* eParent = eRoot;
			var extraAngle = 0;
			while(eSegment)
			{
				var angleAdd = 5 * sinv(mainAngle+extraAngle);
				eSegment.pan = eParent.pan + angleAdd;
				eSegment.tilt = eParent.tilt + angleAdd;
				eSegment.roll = eParent.roll + angleAdd;
				vec_for_vertex(eSegment.x,eParent,15);
				
				eParent = eSegment;
				eSegment = eSegment.nextSegment;
				extraAngle += 10;
			}
		}
		wait(1);	
	}
}


function main()
{
	sky_color.red = 0;
	sky_color.green = 57;
	sky_color.blue = 77; // bright blue sky 
	video_mode = 10;

	random_seed(0);
	d3d_antialias = 9;
	max_entities = 9999;
	level_load(NULL);
	ent_create(CUBE_MDL,nullvector,creature);
	camera.x -= 150;
	def_move();
}



Number of roots is limited (by 80 or 100 if you change the code), segments "unlimited", you could vary their lengths as well.



Personally, I'd make this all one model though, as you want to keep the number of objects low.
One model, let's say 9 tentacles, which are rotated via bones around the origin. The actual tentacle movement would be realized with a vertex shader that does all the heavy lifting, and you're good.
Posted By: txesmi

Re: vec_for_vertex gap issue - 04/19/18 11:55

Hi,
I feel that tentacles thingy appealing and gived a try to an approach with animated polylines and quadratic bezier curves. Probably worse than bones but it has been fun laugh



I use the tentacles skills to store an array of vectors that serve as polyline.

Code:
void tentacleInit(ENTITY *entTentacle) {
	c_setminmax(entTentacle);
	entTentacle->skill99 = floatv(entTentacle->max_x / 8.5); // section length
	int _i = 0;
	for (; _i<9; _i+=1) {
		entTentacle->skill[_i*3] = 50 - _i * 4;
		entTentacle->skill[1+_i*3] = 0;
		entTentacle->skill[2+_i*3] = 0;
		vec_set(&entTentacle->skill[30 + _i * 3], &entTentacle->skill[_i * 3]);
	}
	entTentacle->material = mtl_create();
	effect_load(entTentacle->material, "tentacle.fx");
	entTentacle->material->flags |= ENABLE_TREE;
	entTentacle->material->event = evnTentacle;
}



Those vectors are processed and passes to the material each frame.

Code:
void evnTentacle () {
	float fSpline[32] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
	fSpline[0] = -0.5 * my->skill[0];
	fSpline[1] = -0.5 * my->skill[2]; // swapped X & Z axes
	fSpline[2] = -0.5 * my->skill[1];
	int _i = 1;
	for(; _i<10; _i+=1) {
		fSpline[_i*3] = fSpline[-3+_i*3];
		fSpline[1+_i*3] = fSpline[-2+_i*3];
		fSpline[2+_i*3] = fSpline[-1+_i*3];
		fSpline[_i*3] += (float)my->skill[-3+_i*3];
		fSpline[1+_i*3] += (float)my->skill[-1+_i*3]; // swapped X & Z axes
		fSpline[2+_i*3] += (float)my->skill[-2+_i*3];
	}
	LPD3DXEFFECT fx1 = my->material->d3deffect;
	if (fx1) {
		fx1->SetValue("fSpline", fSpline, 30 * sizeof(float));
		fx1->SetValue("fStep", &my->skill99, sizeof(var));
	}
}



I presented a tentacle along the X axe so I assign the mesh into the polyline according to its position in length.

Code:
float3 fSpline[10];
float fStep;

float3 doSpline(in float depth, out float3 vTangent) {
	depth = max(depth - fStep * 0.5f, 0);
	float index = floor(depth / fStep);
	float factor = fmod(depth, fStep) / fStep;
	float3 v1 = lerp(fSpline[index], fSpline[index+1], 0.5);
	float3 v1b = lerp(v1, fSpline[index+1], factor);
	float3 v2 = lerp(fSpline[index+1], fSpline[index+2], 0.5);
	float3 v2b = lerp(fSpline[index+1], v2, factor);
	vTangent = normalize(v2b - v1b);
	return lerp(v1b, v2b, factor);
}



I use the returned tangent in order to rotate (not really) transverse sections.

Code:
void VS (
	in float4 inPos: POSITION,
	out float4 outPos: POSITION,
	out float3 outNormal: TEXCOORD0) {
		float3 vTangent = 0;
		float3 vPos = doSpline(inPos.x, vTangent);
		outPos.x = vPos.x - vTangent.y * inPos.y - vTangent.z * inPos.z;
		outPos.y = vPos.y + inPos.y;
		outPos.z = vPos.z + inPos.z;
		outPos.w = 1.0f;
		outNormal = normalize(mul(outPos.xyz - vPos, (float3x3)matWorld));
		outPos = mul(outPos, matWorldViewProj);
	}



This simplification needs the tangent to be smaller than 45 degrees from the X axe, so the tentacles has to point, more or less, to the front. Otherway it looks wrong.

Logaritmic distribution of angular speeds and segment lengths beside a single step of forward inverse kinematic make the random animation look pretty soft and natural.

Code:
void tentacleAnimate (ENTITY *entTentacle) {
	int _i = 0;
	for(; _i<9; _i+=1) {
		VECTOR vOff;
		vec_diff(&vOff, &entTentacle->skill[30+_i*3], &entTentacle->skill[_i*3]);
		var _length = 2 * time_step / (9 - _i);
		if (vec_length(&vOff) < _length) {
			entTentacle->skill[30+_i*3] = 10;
			entTentacle->skill[31+_i*3] = random(10+_i*10) - (_i+1)*5;
			entTentacle->skill[32+_i*3] = random(10+_i*10) - (_i+1)*5;
			vec_normalize(&entTentacle->skill[30+_i*3], 50 - _i * 4);
		} else {
			vec_normalize(&vOff, _length);
		}
		vec_add(&entTentacle->skill[_i*3], &vOff);
		vec_sub(&entTentacle->skill[(_i+1)*3], &vOff); // forward inverse kinematic
	}
}



Salud!
Posted By: Dooley

Re: vec_for_vertex gap issue - 04/19/18 20:15

@Superku,
I tried yours, it looks awesome! I love the smooth movement. I will probably keep my version for the game for now, just because I actually understand it and it is working, but thanks. I do plan to study your code in more detail, and hopefully learn a few things from it.

@txesmi
That looks really neat! I will try that out when I have the chance. Thanks for your contribution. It is humbling to see so much more sophisticated code than mine (both you and Superku) so I appreciate the help!
© 2024 lite-C Forums