Massassi Forums Logo

This is the static archive of the Massassi Forums. The forums are closed indefinitely. Thanks for all the memories!

You can also download Super Old Archived Message Boards from when Massassi first started.

"View" counts are as of the day the forums were archived, and will no longer increase.

ForumsCog Forum → Haven't you ever wished you could use touched to find a surface?
Haven't you ever wished you could use touched to find a surface?
2004-08-06, 9:35 AM #1
Well now you can. Presenting the code that will allow a cogger the ability to find if a thing is on a surface.

Please credit me if you use this.

Code:
# Jedi Knight Cog Script
#
# ATTACHED_SURFACE.COG
#
# Gets the surface that a thing is on
#
# [DP]
#

   pos = GetThingPos(GetParam(0));
   thingsec = GetThingSector(GetParam(0));
   onsurface = 0;
   For(i=0; !onsurface; i=i+1)
   {
      onsurface = GetSectorSurfaceRef(thingsec, i);
      For(j=0; j<GetNumSurfaceVertices(GetSectorSurfaceRef(thingsec, i)); j=j+1)
      {
         dot = VectorDot(GetSurfaceNormal(i), VectorNorm(VectorSub(GetSurfaceVertexPos(i, j), pos)));
         If(dot >= .05 || dot <= -.05) onsurface = 0;
      }
      If(i > GetNumSectorSurfaces(thingsec)) onsurface = -1;
   }
   Return;

end

The operation principle is as follows:
    Facts relvent
  • All surfaces in a level must be planear
  • planear means the vertices lie in one 2d field
  • Cosine can be used to find the degrees of an angle between 0-180º
  • A surfaces normal is the direction which it is looking so it is perpendicular to the plane of the surface facing the rendering direction. (ie the lookvector facing inside the sector).
    Operation
  • This cog goes through each surface inside a sector
  • It finds teh dot product (cosine) of the surface normal and each of the surfaces vertices
  • If any of the cosines are not close to 0 (90º) then it is not on that surface
    Reason why it works
  • For a posistion to exist on a surface, it must be planear to the surface as well, to check this, you use the normal of the surface (which is perpendicular to the plane) and since were dealing with looking vectors, the origin of the angle is irrelevnt, and if the normal is also perpendicular to the look vector of the posision to all the vertices, then it must lie in the surface (and inside the verticies for that matter).

I know that that is somewhat long winded and I probably confused people more with it, but its not something easy to explain. This is untested, but logistically it works.
Also, you can use my sector of posistion to find that sector, uses roughly the same priciple.
Code:
# Jedi Knight Cog Script
#
# SECTOR_OF_POSISTION.COG
#
# Gets the sector of a posistion
#
# [DP]
#
   insector = 0;
   pos = GetThingPos(GetParam(0));
   For(i=0; !insector; i=i+1)
   {
      insector = i;
      For(j=0; j<GetNumSectorVertices(i); j=j+1)
      {
         dot = VectorDot(VectorNorm(VectorSub(GetSectorCenter(i), pos)), VectorNorm(VectorSub(GetSectorVertexPos(i, j), pos)));
         If(dot >= 0) insector = 0;
      }
      If(i > GetSectorCount()) insector = -1;
   }
   Return;

[http://forums.massassi.net/html/biggrin.gif]

------------------
Major projects working on:
SATNRT
JK Pistol Mod
Aliens TC

edit - updated code for attached_surface.cog

[This message has been edited by Descent_pilot (edited August 09, 2004).]
Major projects working on:
SATNRT, JK Pistol Mod, Aliens TC, Firearms

Completed
Judgement Day (HLP), My level pack
2004-08-06, 11:20 AM #2
My JK-foo long ago faded, but if those work, you will make many a cogger very happy. I worked for a long time to find a way to do those things, but could never get it to work...

------------------
Dear lady, can you hear the wind blow, and did you know
Your stairway lies on the whispering wind.
:wq!
And when the moment is right, I'm gonna fly a kite.
2004-08-07, 4:47 AM #3
Brilliant!

The math looks right. I will definitely be testing this. I will try to post a reply about my test results by Sunday evening (8-Aug).

Thanks for posting the code.

[http://forums.massassi.net/html/smile.gif]

[This message has been edited by ZeqMacaw (edited August 07, 2004).]
2004-08-08, 11:04 AM #4
Okay, here are my remarks and results about the ATTACHED_SURFACE.COG.

- Bug: GetNumSurfaceVertices(thingsec, i) is not valid; most likely this was intended: GetNumSurfaceVertices(GetSectorSurfaceRef(thingsec, i)). Also, GetSurfaceVertexPos(i, j) would change to GetSurfaceVertexPos(GetSectorSurfaceRef(thingsec, i), j)
- Problem 1: The math is correct IF the player pos were on the surface of the player model; but, the player pos seems to be in the center of the player model. Thus, in my tests, the cog only worked when the player was in the middle of a sector several times bigger than the player model.
- Problem 2: We want to avoid retrieving adjoin surfaces. Using GetSurfaceAdjoin() will not help us here.
- Idea for solving problem 1: Use an invisible "weapon" (weapon template) attached to the player. SaberMaster explains this. The weapon template uses an explosion template that calls an explosion cog (i.e. the modified cog below) on "touching" a surface. Hideki explains this.
- Idea for solving problem 2: For now the cog below uses GetFaceGeoMode() to see if surface is an adjoin. (See NOTE in the cog.)

Here is the cog on which I based my testing. It was transformed little by little from the ATTACHED_SURFACE.COG. From this, I will test my ideas stated above.
Code:
code

startup:
	Sleep(1);
	player = GetLocalPlayerThing();
	// For testing against different surfaces.
	SetPhysicsFlags(player, 0x2000); // Player can fly.
	SetPulse(1);
	return;

pulse:
	pos = GetThingPos(player);
	sectorIndex = GetThingSector(player);
	sectorSurfaceCount = GetNumSectorSurfaces(sectorIndex);
	thingIsTouchingSurface = 0;
	for (sectorSurfaceIndex = 0; 
	     sectorSurfaceIndex < sectorSurfaceCount
			 && thingIsTouchingSurface < 3; 
	     sectorSurfaceIndex = sectorSurfaceIndex + 1)
	{
		thingIsTouchingSurface = 0;
		surfaceIndex = GetSectorSurfaceRef(sectorIndex, sectorSurfaceIndex);
		//NOTE: Want to use GetSurfaceAdjoin() here to avoid testing 
		// adjoin surfaces, but JK crashes if used on non-adjoin surfaces. 
		// Using GetFaceGeoMode() b/c adjoin surfaces will usually use 
		// geoMode = 0 (do not render).
		geoMode = GetFaceGeoMode(surfaceIndex);
		if (geoMode != 0)
		{
			surfaceNormal = GetSurfaceNormal(surfaceIndex);
			surfaceVertexCount = GetNumSurfaceVertices(surfaceIndex);
			for (surfaceVertexIndex = 0; 
				 surfaceVertexIndex < surfaceVertexCount; 
				 surfaceVertexIndex = surfaceVertexIndex + 1)
			{
				//NOTE: This only works when sector is big enough
				// so that the player's pos becomes less significant.
				// Need to use player's surface 'pos', not the 
				// player's (3do center?) 'pos'.
				dot = VectorDot(surfaceNormal, 
					VectorNorm(VectorSub(
					GetSurfaceVertexPos(surfaceIndex, surfaceVertexIndex), 
					pos)));
				if (dot >= -0.05 && dot <= 0.05)
				{
					thingIsTouchingSurface = thingIsTouchingSurface + 1;
				}
			}
		}
	}

	if (thingIsTouchingSurface > 2)
	{
		jkStringClear();
		jkStringConcatAsciiString("Touching surface ");
		jkStringConcatInt(surfaceIndex);
		jkStringConcatAsciiString("   dot = ");
		jkStringConcatFlex(dot);
		jkStringOutput(-1, -1);
	}

	return;

end


Notes:
- I use thingIsTouchingSurface < 3 in the first for loop and if (thingIsTouchingSurface > 2) because only 3 points (vertices) are needed to define a plane.
- The pulse would not be needed when the cog is used as the explosion cog.

[http://forums.massassi.net/html/smile.gif]


[This message has been edited by ZeqMacaw (edited August 08, 2004).]
2004-08-08, 6:14 PM #5
Never assume on cog verbs... (end mental note) *grumbles and curses b/c GBK is laughin thinkin its just like old times*

Prob 1, it was ment to be used for a weapon hitting, such as raildet on sec fire. (I should have noted that [http://forums.massassi.net/html/redface.gif]) That dot > -.5 and < .5 is for a small variation off the surface, its basically its accuracy factor.

Prob 2, that wouldn't have happened in the purpose, so thats why its not included. But also, that way isn't exactly 100% effective for what it might be used for, adding !(IsAdjoinFlags(surface) & 0x1) to check to see if its impassible or not (not sure if that setup is right my brain is fried right now, but you get the idea), for maybe like a forcefield.

Do NOT change the function/flow of my code. Not only is it less lines, but its more reliable (if the codin was right :P), since only checking 3 vertices, in a surface, they could all be in a line, it doesn't nessicarily define one plane. Thus you could have problems, tho small, it could matter.

------------------
Major projects working on:
SATNRT
JK Pistol Mod
Aliens TC

[This message has been edited by Descent_pilot (edited August 08, 2004).]
Major projects working on:
SATNRT, JK Pistol Mod, Aliens TC, Firearms

Completed
Judgement Day (HLP), My level pack
2004-08-09, 2:08 AM #6
Quote:
<font face="Verdana, Arial" size="2">Prob 1,it was ment to be used for a weapon hitting</font>

Heh. I must have a one-track mind. [http://forums.massassi.net/html/biggrin.gif]
I saw the post and immediately thought the calculation would allow a "touched message" for a player instead of having to use a surface cog. (I have that cog almost finished; I'll start a different topic on it.)

Quote:
<font face="Verdana, Arial" size="2">Do NOT change the function/flow of my code. Not only is it less lines, but its more reliable (if the codin was right :P)</font>

I agree that it is fewer lines, but unfortunately your code does not work. I have tested my code; have you tested yours? [http://forums.massassi.net/html/tongue.gif] (Your dot calculation is right; that's the important part.)
The only important change I made to the flow of the program was checking only 3 vertices; that was a bug as you have noted (see the next quote below).

Some more bugs in your code:
  • For(i=0; !onsurface; i=i+1) should really be something like:
    For(i=0; i<GetNumSectorSurfaces(thingsec)) && !onsurface; i=i+1)
    just in case onsurface is not changed to 0 (i.e. the dot calculation was not in the small variation).
  • If(dot >= .05 && dot <= -.05) will never be true; it should be (as it is in my code):
    if (dot >= -0.05 && dot <= 0.05)


Quote:
<font face="Verdana, Arial" size="2">since only checking 3 vertices, in a surface, they could all be in a line, it doesn't nessicarily define one plane. Thus you could have problems, tho small, it could matter.</font>

Yep, that is a bug. I had not thought of the 3 vertices being in a line, which would be a problem if two surfaces in the sector "shared" those 3 vertices. [http://forums.massassi.net/html/redface.gif]
I was trying to reduce the time it takes to make the test.
Another idea is to use GetSurfaceCenter(surface) if it is reasonably fast. We could use the first 2 vertices of the surface and use the surface center as the third vertex. (Because the first two vertices should be "next" to each other for JK to work properly with the surface, the surface center should not be in a line with the first 2 vertices.) Then using the 3 vertices would work (for all planar surfaces) and would likely be faster than using all vertices in a surface with many vertices.

Again, thanks for posting the code.

[http://forums.massassi.net/html/smile.gif]
2004-08-09, 4:01 AM #7
Quote:
<font face="Verdana, Arial" size="2">Originally posted by Descent_pilot:
...*grumbles and curses b/c GBK is laughin thinkin its just like old times*...</font>


Nah, if this were the old says, I would have caught the problems... [http://forums.massassi.net/html/wink.gif]

------------------
Dear lady, can you hear the wind blow, and did you know
Your stairway lies on the whispering wind.
:wq!
And when the moment is right, I'm gonna fly a kite.
2004-08-09, 2:56 PM #8
Quote:
<font face="Verdana, Arial" size="2">Originally posted by ZeqMacaw:
Some more bugs in your code:
    [*]For(i=0; !onsurface; i=i+1)[/b] should really be something like:
    For(i=0; i<GetNumSectorSurfaces(thingsec)) && !onsurface; i=i+1)
    just in case onsurface is not changed to 0 (i.e. the dot calculation was not in the small variation).[/b]</font>
    That if statement after the for(j) catches that, both ways work, its not a bug, just a different way of doing things.

    Quote:
    <font face="Verdana, Arial" size="2">[*][i]If(dot >= .05 && dot <= -.05)[/i] will never be true; it should be (as it is in my code): [i]if (dot >= -0.05 && dot <= 0.05)[/i] [/list]</font>

    (that was suppose to be an or statement [http://forums.massassi.net/html/redface.gif])
    Check the logic flow of the cog, if you have it testing true, then setting the onsurface to 0 makes it go through the process again. That was made so that if its impossible to be true (not 90º and thus not on the plane), then keep going.

    Also, changed a few other logic errors. I need to get more sleep, I'm outta it...

    ------------------
    Major projects working on:
    SATNRT
    JK Pistol Mod
    Aliens TC

    [This message has been edited by Descent_pilot (edited August 09, 2004).]
    Major projects working on:
    SATNRT, JK Pistol Mod, Aliens TC, Firearms

    Completed
    Judgement Day (HLP), My level pack
    2004-08-14, 4:05 PM #9
    Oh! The logic makes sense now. [http://forums.massassi.net/html/smile.gif]

    The ATTACHED_SURFACE.COG is looking better, but it still has bugs:
    1. The symbol "i" is still referenced in the line that calculates "dot"; both references should be changed into: GetSectorSurfaceRef(thingsec, i).
    2. It will not detect surface #0
      [/list=a]

      For efficiency, the code (up to the "Return;") should look like this (but it will still have bug #2 listed above because I did not change the logic) :
      Code:
      pos = GetThingPos(GetParam(0));
      thingsec = GetThingSector(GetParam(0));
      sectorSurfaceCount = GetNumSectorSurfaces(thingsec);
      onsurface = 0;
      For(i=0; !onsurface; i=i+1)
      {
      	surfaceIndex = GetSectorSurfaceRef(thingsec, i);
      	surfaceNormal = GetSurfaceNormal(surfaceIndex);
      	surfaceVertexCount = GetNumSurfaceVertices(surfaceIndex);
      	onsurface = surfaceIndex;
      	For(j=0; j<surfaceVertexCount; j=j+1)
      	{
      		dot = VectorDot(surfaceNormal, VectorNorm(VectorSub(GetSurfaceVertexPos(surfaceIndex, j), pos)));
      		If(dot >= .05 || dot <= -.05) onsurface = 0;
      	}
      	If(i > sectorSurfaceCount) onsurface = -1;
      }


      (I am not picking on you, Descent_pilot, only the code. [http://forums.massassi.net/html/smile.gif] I have been trying to finish my "touched" message and have been using your code for part of it.)

      [http://forums.massassi.net/html/smile.gif]


      [This message has been edited by ZeqMacaw (edited August 14, 2004).]
    2004-08-15, 7:57 AM #10
    Another bug:

    Quote:
    <font face="Verdana, Arial" size="2">if the normal is also perpendicular to the look vector of the posision to all the vertices, then it must lie in the surface (and inside the verticies for that matter)</font>


    The statement in parens is wrong. A position outside the vertices will still give vectors that are perpindicular to the normal. Plus, my tests using the calculation show that dot=0 when I place the point outside the vertices (but on the same plane as the surface).

    What does this mean? The code might give the wrong surface if the correct surface is in the same plane as the wrong one.

    [http://forums.massassi.net/html/smile.gif]
    2004-08-24, 6:20 AM #11
    I understand the theory behind this...but little else. I really hope you get this code working because I can already think of TONS of uses for it.

    Oh, btw I was refering to his point that surfaces that on the same plane could result in false positives, or rather two or more surfaces that meet 'pass the test'. I'm not sure if thats true or not, I'd have to examine the code more and try to remember my trig from 3 years ago.

    Anyway, if that were the case perhaps you could use the knowledge that they are on the same plane to make a decision. You'd have to run through all surfaces in a sector instead of bailing out of the loop when you find a match like you do now.

    But then couldn't you put all matching surfaces into an array, then since you *know* they are all on the same plane...just measure distance from the vertices of each surface to determine which one its actually in?

    Oh man, I just thought about that it would be a mess of code. Lets hope some one has a better idea. I'm just doing a little brainstorming about a solution to a problem I'm not even sure exists. [http://forums.massassi.net/html/biggrin.gif]

    [This message has been edited by Wave_Of_Mutilation (edited August 24, 2004).]
    2004-08-25, 4:12 PM #12
    I am glad someone besides Descent_pilot has paid attention (and responded) to my comments (and math). [http://forums.massassi.net/html/smile.gif] I was starting to think that we were the only two interested in this stuff.

    Some more comments (these will likely be the last, at least in this thread) :
    - Please remember that I have actually tested my code in both single player and multiplayer.
    - I am unclear to which message handler Descent_pilot intended his ATTACHED_SURFACE.COG. If it is intended for "a weapon hitting", I take this to mean it would be for a thing using a template of type=weapon. A weapon cog does not receive the "touched" message when it hits a surface, so his thread title is a little misleading. It does receive a "removed" message if it is set to explode when it hits a surface. However, the "removed" message does not use "Params"; it uses "SenderRef". Thus, another bug in his code. [http://forums.massassi.net/html/frown.gif]
    - Now for the best part...I *have* implemented (and tested in SP and MP) the code for accurate "sector index detection" (as I like to call it). Check the forum for a new topic about "surface index detection" in the next few days. As always, I will start the thread title with a tag (probably "Tip:" or "Idea:" in this case). I can't do it now because I need to clean up the code and comments so it makes sense to others. [http://forums.massassi.net/html/smile.gif] BTW, there is a "lot" of code involved, but it seems to run smoothly without slowing down my two P3-550 machines.

    [http://forums.massassi.net/html/smile.gif]

    ↑ Up to the top!