Jump to content

SexLab Framework Development


Recommended Posts

There would still be no deletion though. If object is deleted then reallllly small chance (less than 0.01%) that a new object is created with same ID that a script would set data on. I guess it wouldn't be a problem if data is never deleted because it only takes up few bytes but doesn't feel good. :\

Link to comment

Yeah, it's at least semi unlikely, it's something at least though.

 

Looking over everything else, I haven't tested any of it yet, trying to finish up some heavy thread optimizations I'm working on now for 1.30. 

sub_848820(dword_1B10B58, "No reference selected"); <- print console message

Not a huge priority by any means, so if it's not a huge amount of work, could you maybe add that as a function as well? Printing messages to console would be a decent solution to all the people wanting a way to see the name of a playing animation or an animations current alignment offsets. Debug.Notification() has proved unreliable for that in the past, and Debug.MessageBoxes too intrusive.

 

 

On all the SetNode functions for position and rotation:

void i_SetNodePositionX(StaticFunctionTag* base, TESObjectREFR * obj, BSFixedString nodeName, bool firstPerson, float newPos)
{
	NiAVObject * object = ResolveNode(obj, nodeName, firstPerson);
	if(object)
	{
		object->m_worldTransform.pos.x = newPos;
		NiAVObject::ControllerUpdateContext ctx;
		object->UpdateWorldData(&ctx);
	}
}

Since each one calls object->UpdateWorldData(&ctx) doing the 3 functions consecutively like this:

SetNodePositionX(PlayerRef, "NPCHead", false, 1853.12)
SetNodePositionY(PlayerRef, "NPCHead", false, -1425.0)
SetNodePositionZ(PlayerRef, "NPCHead", false, 53.25)

The Player's head would effectively bounce between 3 different sets of coordinates as each position is updated. With rotation it would it would be even more pronounced as it would be rotating around 9 of them.

 

The single coordinate/rotation functions could be handy still for doing fine tuning, something like this would probably work best in game when setting all 3:

SetNodePosition(PlayerRef, "NPCHead", false, 1853.12, -1425.0, 53.25)

Updating all 3 coordinates at once, and then doing a single object->UpdateWorldData(&ctx) afterward.

 

 

This would be a bit messier with rotation stuff though, since there is 9 of them, not sure how many arguments SKSE's native functions will let you do. So for rotation maybe just remove the object->UpdateWorldData(&ctx) entirely, and make a separate native UpdateNodeData() function that does nothing but call object->UpdateWorldData(&ctx) on a node. 

Link to comment

Hmm yeah good idea, same can't be done with rotation because max arguments is 10,  maybe best to add the updateworlddata as separate function instead. There are a lot more things I found in source so far too, I'll write them to the text file in next one. One interesting thing I found is "AttachPapyrusScript" function :D that could be fun, but it's a bit complicated to understand :P

Link to comment

Hmm yeah good idea, same can't be done with rotation because max arguments is 10,  maybe best to add the updateworlddata as separate function instead. There are a lot more things I found in source so far too, I'll write them to the text file in next one. One interesting thing I found is "AttachPapyrusScript" function :D that could be fun, but it's a bit complicated to understand :P

 

Is there also a "DetachPapyrusScript" ? If so, it would make for an intresting way to cut down on a lot of limitations, to use SexLab as an example, could attach an animation script directly onto an actor to lock them in place and begin the animation, this would pretty much remove the need for so many reference aliases and keep things from ever being able to overlap when they should not as a result.

 

Would only be good though if it could also remove itself afterwards though.

Link to comment

 

For code simplicity sake, I'd probably make it

SendThreadEvent("AnimationChange", (backwards as int))

that way it would send 0 for forward and 1 for backward.

 

Alternatively I could do this:

 SendThreadEvent("AnimationChange", aid)

sending the actual index id of the animation, which Controller.SetAnimation(int animid) will take as an argument.

 

Yeah that does the trick too. I'm just used to write verbose code :)

 

Correct me, if I'm wrong. As far as I understood your code, the aid is the int of the next animation stage which will played? If so, then the first variant would be better since then we can call the previous/next animation stage based on the bool. Else we would need to hold another int to see what the current animation stage was.

Link to comment

No once added it couldn't be removed, at least don't think there's a game function for this.

 

Ok made the changes to node stuff and added printconsole function.

 

 

Just tried some light testing on everything before I head to bed, neither SetNodePosition or SetNodeRotation seem to work in game.

 

My testing script:

	Debug.TraceAndBox("HasNode: "+NetImmerse.HasNode(PlayerRef, "NPC Rotate [Rot ]", true))
	Utility.Wait(4.0)

	float[] rot = new float[9]
	rot[0] = SexLabUtil.GetNodeRotation(PlayerRef, "NPC Rotate [Rot ]", true, 0)
	rot[1] = SexLabUtil.GetNodeRotation(PlayerRef, "NPC Rotate [Rot ]", true, 1)
	rot[2] = SexLabUtil.GetNodeRotation(PlayerRef, "NPC Rotate [Rot ]", true, 2)
	rot[3] = SexLabUtil.GetNodeRotation(PlayerRef, "NPC Rotate [Rot ]", true, 3)
	rot[4] = SexLabUtil.GetNodeRotation(PlayerRef, "NPC Rotate [Rot ]", true, 4)
	rot[5] = SexLabUtil.GetNodeRotation(PlayerRef, "NPC Rotate [Rot ]", true, 5)
	rot[6] = SexLabUtil.GetNodeRotation(PlayerRef, "NPC Rotate [Rot ]", true, 6)
	rot[7] = SexLabUtil.GetNodeRotation(PlayerRef, "NPC Rotate [Rot ]", true, 7)
	rot[8] = SexLabUtil.GetNodeRotation(PlayerRef, "NPC Rotate [Rot ]", true, 8)
	Debug.TraceAndBox("Before: "+rot)
	Utility.Wait(1.0)

	SexLabUtil.SetNodeRotation(PlayerRef, "NPC Rotate [Rot ]", true, 0, 1.0)
	SexLabUtil.SetNodeRotation(PlayerRef, "NPC Rotate [Rot ]", true, 1, 0.0)
	SexLabUtil.SetNodeRotation(PlayerRef, "NPC Rotate [Rot ]", true, 2, 0.0)
	SexLabUtil.SetNodeRotation(PlayerRef, "NPC Rotate [Rot ]", true, 3, 0.0)
	SexLabUtil.SetNodeRotation(PlayerRef, "NPC Rotate [Rot ]", true, 4, -1.0)
	SexLabUtil.SetNodeRotation(PlayerRef, "NPC Rotate [Rot ]", true, 5, 0.0)
	SexLabUtil.SetNodeRotation(PlayerRef, "NPC Rotate [Rot ]", true, 6, 0.0)
	SexLabUtil.SetNodeRotation(PlayerRef, "NPC Rotate [Rot ]", true, 7, 0.0)
	SexLabUtil.SetNodeRotation(PlayerRef, "NPC Rotate [Rot ]", true, 8, -1.0)
	SexLabUtil.UpdateNode(PlayerRef, "NPC Rotate [Rot ]", true)
	Utility.Wait(1.0)
	
	rot[0] = SexLabUtil.GetNodeRotation(PlayerRef, "NPC Rotate [Rot ]", true, 0)
	rot[1] = SexLabUtil.GetNodeRotation(PlayerRef, "NPC Rotate [Rot ]", true, 1)
	rot[2] = SexLabUtil.GetNodeRotation(PlayerRef, "NPC Rotate [Rot ]", true, 2)
	rot[3] = SexLabUtil.GetNodeRotation(PlayerRef, "NPC Rotate [Rot ]", true, 3)
	rot[4] = SexLabUtil.GetNodeRotation(PlayerRef, "NPC Rotate [Rot ]", true, 4)
	rot[5] = SexLabUtil.GetNodeRotation(PlayerRef, "NPC Rotate [Rot ]", true, 5)
	rot[6] = SexLabUtil.GetNodeRotation(PlayerRef, "NPC Rotate [Rot ]", true, 6)
	rot[7] = SexLabUtil.GetNodeRotation(PlayerRef, "NPC Rotate [Rot ]", true, 7)
	rot[8] = SexLabUtil.GetNodeRotation(PlayerRef, "NPC Rotate [Rot ]", true, 8)
	Debug.TraceAndBox("After: "+rot)

Results of the output:

 

HasNode: TRUE

Before: [-0.488371, -0.869316, 0.076055, 0.872636, -0.486512, 0.042564, 0.000000, 0.087156, 0.996195]
After: [-0.488371, -0.869316, 0.076055, 0.872636, -0.486512, 0.042564, 0.000000, 0.087156, 0.996195]

 

 

So seems to be getting the matrix just fine, but no effects from altering it. SetNodePosition() had similar results.

 

PrintConsole() and SetFreeCameraSpeed() worked without a hitch. As did the bit of testing the variable stuff I did.

Link to comment

It's possible this position gets updated every frame, so when you set it to something it gets immediately overwritten even if it's with the same values. But if you mean to use this for camera setting I don't like this method at all I found a way to overwrite camera position and rotation from C code, trying to think of a way now that I can integrate this into game code.

 

So that it could be something like this:

SetCameraNode(Game.GetPlayer(), "NPCEyeBone") or whatever :P and from there on camera position and rotation is always that node's position and rotation (or multiplying multiple node rotation if necessary) with no extra code needed in papyrus other than to later reset back to normal.

Link to comment

It's possible this position gets updated every frame, so when you set it to something it gets immediately overwritten even if it's with the same values. But if you mean to use this for camera setting I don't like this method at all I found a way to overwrite camera position and rotation from C code, trying to think of a way now that I can integrate this into game code.

 

So that it could be something like this:

SetCameraNode(Game.GetPlayer(), "NPCEyeBone") or whatever :P and from there on camera position and rotation is always that node's position and rotation (or multiplying multiple node rotation if necessary) with no extra code needed in papyrus other than to later reset back to normal.

 

Would it still have look controls though? or be locked staring straight ahead? With the current setup the player can still look around with their mouse during first person animation, as if moving their head around, the idea to rotate the nodes was more to properly orient the center of those look controls.

Link to comment

Correct me, if I'm wrong. As far as I understood your code, the aid is the int of the next animation stage which will played? If so, then the first variant would be better since then we can call the previous/next animation stage based on the bool. Else we would need to hold another int to see what the current animation stage was.

 

 

 

aid is the index of the sslBaseAnimation that is playing at any given moment, relative to the selected animations for a scene.

; Animation
int aid
sslBaseAnimation property Animation hidden
	sslBaseAnimation function get()
		return Animations[aid]
	endFunction
endProperty

ChangeAnimation() is basically nothing more than a +/- 1 to aid

Link to comment

I ran into a problem. I have function:

function SetCameraNode(ObjectReference obj, string posNodeName, string rotNodeName, bool firstPerson = false) global native

 

Position is working but I don't know how to set rotation correctly.

 

Let's say:

float * overrideRotation = NPCEyeBone.rotation; // float[9]

 

Skyrim sets rotation on camera like this:

*(float *)(a1 + 1744) = *(float *)a8;
*(float *)(a1 + 1748) = *(float *)a7;
*(float *)(a1 + 1752) = *(float *)a6;
*(float *)(a1 + 1756) = 0.0;
*(float *)(a1 + 1760) = *(float *)(a8 + 4);
*(float *)(a1 + 1764) = *(float *)(a7 + 4);
*(float *)(a1 + 1768) = *(float *)(a6 + 4);
*(float *)(a1 + 1772) = 0.0;
*(float *)(a1 + 1776) = *(float *)(a8 + 8);
*(float *)(a1 + 1780) = *(float *)(a7 + 8);
*(float *)(a1 + 1784) = *(float *)(a6 + 8);
*(float *)(a1 + 1788) = 0.0;

 

So I made that:

float * a6 = overrideRotation;

float * a7 = a6 + 0xC;

float * a8 = a7 + 0xC;

 

But it's not right, either the eye bone rotation can't be copied to camera or I made a mistake somewhere, I tried different combinations too like switching a8 and a6 etc. I don't know how a matrix works so I don't know what to do here :s

 

This would fix rotation on a certain point but I still want to do this because I can later add to this rotation the default rotation so that the camera is controlled by the node but you can still look around.

Link to comment

Unpredictably, like looking at ceiling.. but maybe rotation isn't necessary after all look at this:

 

 

norotation.png

 

 

 

This is without rotation, I can look and my character follows mouse as long as weapon is drawn, if not drawn then I can look behind me lol.. also the clipping issue needs to be fixed.

Link to comment

Unpredictably, like looking at ceiling.. but maybe rotation isn't necessary after all look at this:

 

 

norotation.png

 

 

 

This is without rotation, I can look and my character follows mouse as long as weapon is drawn, if not drawn then I can look behind me lol.. also the clipping issue needs to be fixed.

 

That's exactly why it is needed, the current 1st person implementation has the same result as that, albeit with a much slower papyrus method.

 

It looks normal there because your character is standing up, but say your character was laying flat on their back during an animation or facing straight down on their knees, that's when it becomes really disorienting and looks strange. As if you're body moved completely independent of your head, the head always perfectly straight looking forward, while the body contorts around it.

 

Try starting an animation where the players head is at a less normal angle/direction and then doing it, it should make sense then.

Link to comment

Yes you are right. But I don't know how to get the head rotation or at least I don't know how to set it to camera rotation, I'll try more combinations of setting the rotation values.

        // v6?
        overrideCamera[3] = rotObject->m_worldTransform.rot.data[2];
        overrideCamera[4] = rotObject->m_worldTransform.rot.data[5];
        overrideCamera[5] = rotObject->m_worldTransform.rot.data[8];

        // v7?
        overrideCamera[6] = rotObject->m_worldTransform.rot.data[1];
        overrideCamera[7] = rotObject->m_worldTransform.rot.data[4];
        overrideCamera[8] = rotObject->m_worldTransform.rot.data[7];

        // v8?
        overrideCamera[9] = rotObject->m_worldTransform.rot.data[0];
        overrideCamera[10] = rotObject->m_worldTransform.rot.data[3];
        overrideCamera[11] = rotObject->m_worldTransform.rot.data[6];

There's a lot of combinations that the values can be set here and each requires to recompile, start skyrim, load game, cast spell, see that it's wrong, go back to code. :( And what's worse is I don't even know if there's a right combination here or not because maybe the node rotation can't be converted to camera so easily.

Link to comment

I've been able to make the camera in game rotate properly with papyrus by spawning an invisible chair at an actors eye node, the chair automatically matches the rotation, and then making the player use the chair orients their camera with the chair. It only worked in limited testing though, soon as I tried to implement it into the first person mode, it became too unwieldy to really be feasible and still keep the movement smooth looking.

 

As for the clipping, I don't know if there is a solution to that, I've tried just about everything I could there from a papyrus level, if you can find a way to reduce the clipping distance in C, than that should do the trick.

Link to comment

Ok I tried all combinations of setting the values, this was the closest to working only thing that was wrong is that it looks up and down instead of left and right:

overrideCamera[9] = rotObject->m_worldTransform.rot.data[7]; // a8
overrideCamera[6] = rotObject->m_worldTransform.rot.data[4]; // a7
overrideCamera[3] = rotObject->m_worldTransform.rot.data[1]; // a6

overrideCamera[10] = rotObject->m_worldTransform.rot.data[8]; // a8 + 4
overrideCamera[7] = rotObject->m_worldTransform.rot.data[5]; // a7 + 4
overrideCamera[4] = rotObject->m_worldTransform.rot.data[2]; // a6 + 4

overrideCamera[11] = rotObject->m_worldTransform.rot.data[6]; // a8 + 8
overrideCamera[8] = rotObject->m_worldTransform.rot.data[3]; // a7 + 8
overrideCamera[5] = rotObject->m_worldTransform.rot.data[0]; // a6 + 8

What would I have to change to make it look left and right?

 

These are the combinations I tried:

012

345

678

 

012

678

345

 

345

012

678

 

345

678

012

 

678

012

345

 

678

345

012

 

And also same with:

210 543 876

036 147 258

630 741 852

 

So I tried 24 combinations, but rotation is wrong on that one I posted. Do I have to set something negative or? I really have no idea about how this rotation works.

Link to comment

Ok I found a right one but there is just one thing wrong, the view is rotated 90 degrees left like you tilt your head left.

 

overrideCamera[9] = rotObject->m_worldTransform.rot.data[7]; // a8
overrideCamera[6] = rotObject->m_worldTransform.rot.data[4]; // a7
overrideCamera[3] = rotObject->m_worldTransform.rot.data[1]; // a6

overrideCamera[10] = rotObject->m_worldTransform.rot.data[6]; // a8 + 4
overrideCamera[7] = rotObject->m_worldTransform.rot.data[3]; // a7 + 4
overrideCamera[4] = rotObject->m_worldTransform.rot.data[0]; // a6 + 4

overrideCamera[11] =
-rotObject->m_worldTransform.rot.data[8]; // a8 + 8
overrideCamera[8] =
-rotObject->m_worldTransform.rot.data[5]; // a7 + 8
overrideCamera[5] =
-rotObject->m_worldTransform.rot.data[2]; // a6 + 8
 

Look at the last group if I put the "-" sign in front of those then head is tilted right (90 degree right) so I need to somehow calculate what's in middle, but I don't know how. You do it :P

Link to comment

I did some testing in game, and I can confirm the order of the rot matrix indexes is as one would expect.

 

I spawned a marker in game, moved it to the NPCEyeBone node so it'd match it's rotation.

 

Got the Matrix of the node using the GetNodeRotation() function, as well as the xyz angles of the placed marker, placed the resulting xyz angles into nifskope and when you hover over the values on nifskope you can see the resulting matrix. The matrix output in nifskope was identical to the one I got from GetNodeRotation(), so the indexes of rot.data are like this:

 0 1 2
 3 4 5
 6 7 8

For reference, this is what the rotation I tested with looks like:

 

In Skyrim:

X (yaw): 0.239647

Y (pitch): -0.235491

Z (roll/rotation): -148.766068

 

The matrix:

  -0.838338, -0.543751,  0.039036,
   0.525071, -0.824642, -0.210396,
   0.146594, -0.155886,  0.976837

Those very likely don't match up perfectly, as there is script lag between pulling the xyz and the matrix, time where there node is moving around slightly. 

 

In NIfSkope:

Entered the above the XYZ as euler values onto a skeleton in nifskope

 

Y: 0.23

P: -0.23

R: -148.76

 

The matrix:

  -0.8550,  0.5186, -0.0040
  -0.5186, -0.8550, -0.0040
  -0.0055, -0.0014,  1.0000

Similar enough to the skyrim results that I'm willing to pass it off  as being due to the the less precise floating point inputs in nifskope and variations to the rotation during papyrus script lag.

 

No idea if any of this helps. But if you can output what the camera rotation is, I can probably match up what order overrideCamera is wanting it to be when compared to data.rot

 
Link to comment

I don't understand anything anymore :\

 

I looked rotation matrix on wiki, this is some BS there are like 3 numbers for whole paragraph of math.

 

I don't know what I have to do, I tried like almost all combinations to put the numbers in order and none of them worked but they did change a lot.

 

Also output camera rotation?

 

Can you tell from this which is right order?

overrideCamera[9] = rotObject->m_worldTransform.rot.data[7]; // a8
        overrideCamera[6] = rotObject->m_worldTransform.rot.data[4]; // a7
        overrideCamera[3] = rotObject->m_worldTransform.rot.data[1]; // a6

        overrideCamera[10] = rotObject->m_worldTransform.rot.data[6]; // a8 + 4
        overrideCamera[7] = rotObject->m_worldTransform.rot.data[3]; // a7 + 4
        overrideCamera[4] = rotObject->m_worldTransform.rot.data[0]; // a6 + 4

        overrideCamera[11] = rotObject->m_worldTransform.rot.data[8]; // a8 + 8
        overrideCamera[8] = rotObject->m_worldTransform.rot.data[5]; // a7 + 8
        overrideCamera[5] = rotObject->m_worldTransform.rot.data[2]; // a6 + 8

arguments are passed to function like this: a6, a7, a8

I set a6 to overrideCamera + 0xC, a7 to a6 + 0xC, a8 to a7 + 0xC

 

Skyrim sets them in this order:

[0] = a8

[1] = a7

[2] = a6

[3] = a8 + 4

[4]= a7 + 4

[5] = a6 + 4

[6] = a8 + 8

[7] = a7 + 8

[8] = a6 + 8

but I don't know if the last array is in same order as rotation on the object.

Link to comment

I'm almost certain this is right, looking at code and how the function is called:

 

 

        overrideCamera[9] = rotObject->m_worldTransform.rot.data[0]; // a8
        overrideCamera[6] = rotObject->m_worldTransform.rot.data[3]; // a7
        overrideCamera[3] = rotObject->m_worldTransform.rot.data[6]; // a6

        overrideCamera[10] = rotObject->m_worldTransform.rot.data[1]; // a8 + 4
        overrideCamera[7] = rotObject->m_worldTransform.rot.data[4]; // a7 + 4
        overrideCamera[4] = rotObject->m_worldTransform.rot.data[7]; // a6 + 4

        overrideCamera[11] = rotObject->m_worldTransform.rot.data[2]; // a8 + 8
        overrideCamera[8] = rotObject->m_worldTransform.rot.data[5]; // a7 + 8
        overrideCamera[5] = rotObject->m_worldTransform.rot.data[8]; // a6 + 8

 

 

 

But in game view is stuck on ceiling. Perhaps the "null" position of head node is not same as "null" position of camera? For xample camera could be facing forward but head node is facing up.. kinda makes sense if you think about it. If that is true then there will be needed some matrix calculations in the function but I'm not touching that, looked at 6 tutorials on it and complete nonsense to me. If you want to do this in papyrus I can convert to C code. Now I'm leaving it like this because I can't progress further.

Link to comment

Ok it was bugging me a lot that I couldn't get it to work :D so I calmed down a bit and looked again. It turns out there is no weird indexing, just copy one array to another. The weird camera angle is due to something else. Here are the values you asked (I think this is what you meant?):

 

Camera original rotation (just 3rd person normal camera from behind):
[0] 0.829037
[1] 0
[2] -0.559194
[3] 0
[4] 1
[5] 0
[6] 0.559194
[7] 0
[8] 0.829037

NPC Head [Head] rotation:
[0] 0.68956
[1] -0.713854
[2] -0.122145
[3] 0.724223
[4] 0.678981
[5] 0.120363
[6] -0.00298751
[7] -0.171458
[8] 0.985187
 

 

Look how different they are. Can you do something with this information?

Link to comment

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...

Important Information

We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue. For more information, see our Privacy Policy & Terms of Use