Ragdoll Mass Editor for Unity

I've recently run into a situation where I needed to alter the mass for each part of a humanoid ragdoll in my Unity game. I had blindly accepted the default mass of '20' in the ragdoll wizard and Unity happily apportioned that 20 mass units across the various parts of the ragdoll. I'm not sure what unit of measure we should pretend Unity uses that makes sense to apply 20 of to represent an adult humanoid, but here we are.

Anyway, I wrote the following script to create an editor that would allow you to choose an object representing the highest level ancestor of your ragdoll, then choose a new mass. It then collects the current mass total, creates a ratio, then applies that ratio to each of the child rigidbodies.

 * a copy of this software and associated documentation files (the
 * the following conditions:
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
using UnityEngine;
using UnityEditor;
namespace Pyehouse.Editors
	public class RagdollMassAdjuster : EditorWindow
		// Can be anything, make it smaller if you like...
		// just preventing divide by zero
		private const float SMALL_MASS_EPSILON = 0.00001f;
		private static bool TooSmall(float m)
			return System.Math.Abs(m) < SMALL_MASS_EPSILON;
		private static RagdollMassAdjuster editor;
		private static int width = 350;
		private static int height = 300;
		private static int x = 0;
		private static int y = 0;
		private bool noRigidbodies = false;
		private bool currentMassTooSmall = false;
		private float newMass = 20f;
		private GameObject ragdollParent;
		private void Awake()
			ragdollParent = Selection.activeGameObject;
			Rigidbody[] rigidbodies = ragdollParent.GetComponentsInChildren<Rigidbody>();
			if (rigidbodies != null && rigidbodies.Length > 0)
				float mass = 0f;
				foreach (Rigidbody rigidbody in rigidbodies)
					mass += rigidbody.mass;
				currentMassTooSmall = TooSmall(mass);
				newMass = mass;
				noRigidbodies = false;
				newMass = 20f;
				noRigidbodies = true;
		[MenuItem("Window/Pyehouse/Ragdoll Mass Adjuster")]
		static void ShowEditor()
		private static void CenterWindow()
			editor = EditorWindow.GetWindow<RagdollMassAdjuster>();
			x = (Screen.currentResolution.width - width) / 2;
			y = (Screen.currentResolution.height - height) / 2;
			editor.position = new Rect(x, y, width, height);
			editor.maxSize = new Vector2(width, height);
			editor.minSize = editor.maxSize;
		private void OnGUI()
			if (currentMassTooSmall)
				EditorGUILayout.HelpBox(string.Format("Current mass of selected ragdoll is too small: {0}", newMass), MessageType.Error);
			else if (noRigidbodies)
				EditorGUILayout.HelpBox("No rigidbodies present in selected GameObject or children.", MessageType.Error);
				EditorStyles.label.wordWrap = true;
				EditorGUILayout.HelpBox("The new mass will be applied, maintaining the same per-object ratio as currently exists.", MessageType.Info);
				EditorGUILayout.HelpBox("So if your head is currently 3 out of a total 20 and you want a total mass of 100, your head will become 15.", MessageType.Info);
				newMass = EditorGUILayout.FloatField("New Mass for Ragdoll:", newMass);
				if (GUILayout.Button("Update Mass"))
		private void UpdateMass(float toMass)
			if (ragdollParent == null) return;
			Rigidbody[] rigidbodies = ragdollParent.GetComponentsInChildren<Rigidbody>();
			if (rigidbodies == null || rigidbodies.Length < 1)
				Debug.LogErrorFormat("No rigidbodies found on selected object.");
			float fromMass = 0f;
			foreach (Rigidbody rigidbody in rigidbodies)
				fromMass += rigidbody.mass;
			if (TooSmall(fromMass))
				Debug.LogErrorFormat("Current mass too small: {0}", fromMass);
			float ratio = toMass / fromMass;
			foreach (Rigidbody rigidbody in rigidbodies)
				rigidbody.mass *= ratio;

cscreen source released under GPL v3

I've made the cscreen source available at github under GPL v3.

You use at your own risk, as always, but if anyone would like to offer updates or would like to take it and run with it, the source is now freely available.

Sorry about the wait. 🙂


Weblogic, Kerberos, Single Sign On and You




Configuring Single Sign-On with Microsoft Clients

Enable Kerberos With Workspace



  1. In the Active Directory create a user account which will be doing 'Kerberos things'. e.g. svc_someapp_krb
    2. 'ktpass' will create an SPN for you, but you can also do it yourself. The instructions suggest creating the SPN separately. It matters little.
    3. Keep in mind what encryption type to select in AD for the user. It must be consistent. We used AES 128. If you go higher than this remember you'll need to download Java Cryptography Extension (JCE) Unlimited Strength Jurisdiction Policy Files here and install them properly.
    5. Sometimes Java implementations for keytabs have problems with special characters. What is a special character? We wound up using strictly alphanumerics to avoid the problem. We couldn't find a firm definition of what was allowed. You could try underscores and hyphens. Much more than that and you begin risking running into this issue.
  2. For our 'ktpass' our principal was of the form 'HTTP/'. Notice the caps? You'll need that. Notice the repetition of the domain? You'll need that to. So our 'ktpass' was like:
    1. ktpass /princ HTTP/ /pass domainuserpassword  /mapuser /ptype KRB5_NT_PRINCIPAL /out weblogic.keytab /crypto AES128-SHA1 /kvno 0
  3. If you are not using node manager, but are running your Weblogic server as a Windows service, you presumably already set up your install.cmd file. You'll need to add the specified options as JAVA_OPTIONS here. Remember, the Server->Server Start arguments are only useful when the Node Manager is handling things on a remote server. For local setup you'll need to use different means. We ended up using full paths for our various files.
  4. If you don't have access to the C:\Windows\ folder, you'll need to create your krb5.ini file and place it somewhere else, then add a startup option to point to it as the default location can't be used. You can do this via the following additional JAVA_OPTIONS parameter:


So what went wrong next? For starters, our inexperience with Weblogic led to a lack of understanding of how the authentication system works. Let's take a look at that sample servlet in the first link. The weblogic.xml snippet (well, it's complete but tiny for sample purposes) looks like this:


If you understand this part, please skip it. This is just saying that "if something in the Weblogic security layer declares that the current visitor is a 'negotiateAdmin' thing or an 'Administrators' thing, then for this servlet we will treat it is a 'negotiateAdminRole' thing". I'm dumbing it down a bit, but given our inexperience with Weblogic when we were setting things up, we didn't yet know how Weblogic would determine that the current user was a 'negotiateAdmin' or an 'Administrators' member. We had the notion of an LDAP or AD (via LDAP) lookup but nothing had yet crystallized.

Now for a snippet from the sample web.xml:

            <description>no description</description>

Starting from the bottom and going up, check out the auth-method. BASIC is going to trigger a login prompt. We want single sign on and this page is meant to be an example of such using Kerberos. You want CLIENT-CERT here, not BASIC. CLIENT-CERT says "hey, browser, you are GOING to give me a certificate of some kind, including possibly a Kerberos ticket, or you will be denied". Quite simple. I suppose the sample was intended to give a visual cue but typically a browser would be configured to do that anyway if an 'Authorization: Negotiate' header came back from the server so again I'm not sure why this change was left in. Anyhow...



Let's jump ahead a bit. Because of the NegotiateIdentityProvider in our security realm, Weblogic knew what our account name was due to the Kerberos ticket authentication taking place. So user 'jsmith' comes along and now the current user is identified as user 'jsmith'. But 'jsmith' doesn't belong to any roles and gets no principals assigned. Weblogic will take the user account and check with other providers to see if they know who 'jsmith' is and see if it has any group/role memberships.

It turns out that to make the sample servlet work, you can simply add a user to the built in Weblogic Users/Groups with the same account name as an account from the Kerberos domain, in this case 'jsmith'. Now add 'jsmith' to a Weblogic group as used in the sample servlet, e.g. 'Administrators'. That will immediately make the sample servlet start working assuming you are 'jsmith'. Moreover, it means that 'jsmith' will become an Administrator when hitting the Weblogic console so you now have single sign on working for your console.

But now the next part comes in, linking to your Active Directory implementation for LDAP lookups. Just as you added the NegotiateIdentityProvider, you can also add an ActiveDirectoryAuthentication provider as well as LDAPAuthentication. We read a number of articles suggesting that even if you are targeting Active Directory, you may still want to use LDAPAuthenticator to work around some problems. We in fact tried LDAP first but ended up getting ActiveDirectoryAuthenticator to work for us. The trick boils down to what you use for the Principal name and the various filters. You need an account you can log in with, preferably a service account of some kind. Some suggested you could log in with the Principal set to just the account name. We ended up having to use the full distinguished name. As for your filters, you'll have to consult with your AD admin, or be capable of examining your LDAP structure to draw your own conclusions. Keep in mind that if you don't fill in the fields for the 'All Users' and 'All Groups' filters, then the Users/Groups tabs will not be auto populated with the users/groups from AD.


Filed under: development No Comments

Have You Seen This SQL Server Bug?

I may have found a bug regarding how SQL Server calls CLR functions when using columns from tables joined using non-indexed columns. I'm including a SQL script, with comments on what to do with it, and hopefully anyone reading this will try it for themselves and tell me if what I'm seeing happens for them and whether I'm misinterpreting things. I'm willing to admit if I'm misunderstanding how things are supposed to work but so far everyone that I have spoken with about this is as perplexed as I am. And yet none of us can imagine this has never been noticed before and also is working as intended.








I'll summarize what I saw before I dump the script.




Aside from simple stating the obvious, that the lack of an INDEX forces a full table scan and the database is giving the CLR function a shot on each row before it is filtered out by the JOIN restriction (though, oddly enough, after any WHERE clause), I can't see why this would happen. Wrapping it in a UDF seems to use a slightly different logic path. Perhaps it is a bug in the CLR assembly from Microsoft (this is Microsoft's geography implementation) but I don't have any other assemblies to test and our environment doesn't allow us to install anything else.

Anyhow, here's the script to reproduce the problem.

 * Only run this once
DROP FUNCTION [dbo].[fMakeGeography]
CREATE FUNCTION [dbo].[fLPPTestMakeGeography]
    @p1 varchar(max)
    , @p2 int
RETURNS geography
    DECLARE @Result geography
    -- just a wrapper around the geography::STGeomFromText call
    RETURN @Result
 * Likewise this sets up the test table.
 * any JOIN statements.
drop table dbo.LPPTestEnvelope
create table dbo.LPPTestEnvelope
id int not null
, geog int
, minx decimal(38,16)
, maxx decimal(38,16)
, miny decimal(38,16)
, maxy decimal(38,16)
select 1,-80,-70,20,30,4326 union all
select 2,-80,-80,20,20,NULL union all
select 3,-80,-75,20,25,4326 union all
select 5,-85,-70,25,40,4326 union all
select 7,-100000,-90000,3000000,3100000,4326
-- select * from dbo.LPPTestEnvelope
set nocount on;
-- in a more complex setting this table variable
-- key values which would be joined to the original data
-- table to get the actual rows desired
declare @envtbl table
    eid int not null primary key
INSERT INTO @envtbl (eid)
    dbo.LPPTestEnvelope env
    -- exclude invalid SRID values
    -- avoid precision bug
    -- avoid crossing hemispheres
    -- display the raw data from the table
    -- directly invoke the CLR to create a geography object
            + '))', env.geog)
            + '))',env.geog)
    dbo.LPPTestEnvelope env
    INNER JOIN @envtbl et on
--  -- exclude invalid SRID values
--  env.geog=4326
--  -- avoid precision bug
--  -- avoid crossing hemispheres
Filed under: development No Comments

Short Term Pain for Long Term Gain


Let's overlook the absurdity of telling them how they can spend their money for a moment. After all, if that were an issue we should be storming the gates of every rich person out there who lives the playboy/playgirl lifestyle and does precious little for their fellow human and we don't see that. As far as I'm concerned the fact that this couple is spending any of their money on social reform is a good thing and to be lauded.




Filed under: personal No Comments

New Stalemate Update Is Available

Like it says on the tin, the new update for Stalemate is available, get it while it's hot!

Also, it's free.. well the update was free, but now I've made the app free too. And Centripetal as well! Y'all have fun!


Stalemate Updates Coming Soon



Splash screen for Stalemate iPhone app

Splash screen for Stalemate iPhone app


Update - We're now live with the new look. In addition, I've made Stalemate and Centripetal free. I think I would rather see them getting played than continue to see a trickle of income off of them. Have fun!

Filed under: development No Comments

Centripetal Re-released!

Greetings and salutations and welcome to 2013! For my first new post of the year I am very happy to announce that I have re-released Centripetal onto the iTunes App Store under my own name. 🙂 Click here to purchase Centripetal on the iTunes App Store.


Now cocos2d 2.0 is out, XCode is at 4.5, iOS is up to v6.x and OpenGL ES 2.0 is en vogue on our favorite mobile platform. As a result, it wasn't enough to just recompile and resubmit. I ended up upgrading cocos2d, box2d, CocosDenshion and the other related libs. I also needed to tweak some of the code that I happened to have which made use of the GL_POINTS_SMOOTH parameter in the call to glEnable. By tweak I mean remove. You'll see that on the help screens where the dot indicator showing which page you are on is now a square and not a circle. Yay progress?!


Anyhow, if you already have your original copy, know that this is purely a re-release. If you haven't tried it out before, you can check out my Centripetal page here (or click the link at the top of the page in the header) and watch the gameplay video.

Filed under: development No Comments



  • Cut all tax rates by 20% in all brackets
  • Eliminate a number of deductions

Note this is not the same as his five point plan. I'm just talking about the general tax related portions. I don't see how this works and I'm not the only one.


Thing is, the US government is pretty hungry and wants that pie. You've made it smaller. We're not revenue neutral. So now we eliminate some deductions. This would add some pie back into the mix. But, and here's a problem that Romney has yet to address, he has not told us what deductions he would suggest eliminating. That's pretty important. At one point, he suggested possibly eliminating the mortgage interest deduction. While his campaign later backed off the idea, that would have been problematic as it would disproportionately affect the middle class he says he doesn't want to hurt. How? Simple... the mortgage interest deduction represents, in relative terms, a larger write off for middle class households than upper class. Take that away and that family is now paying a higher amount of their own money in taxes than they were before. But it gets better.


The final piece of the puzzle is stimulating the economy. This is where Romney pitches his various plans to help create jobs and increase trade. With more money earned, more taxes will be paid. Great! Except capital gains are now off the table, so the business owners who are now making more money aren't paying any more in taxes. So who is picking up the tab here? Oh, the middle and lower classes of income earners. The ones Romney says he doesn't want to make pay more.

Now, whether you think it is fair that 5% of Americans are responsible for providing 60% of the tax revenue is an entirely different question. The point here is that Romney's plan does not work as he says it does. And the biggest tell is that he refuses to provide any details. There's a lot of hand waving but when it comes down to it, his plan is less of a plan than a wishlist. If he starts laying out details and those details make sense, I'll be the first to agree that it is a workable solution. But until then all I've seen are smoke and mirrors.


Filed under: personal 2 Comments

Come on Apple… Amaze Me

So you might have heard about a new phone that's out now. They call it the iPhone 5 and it is apparently the best phone Apple has ever created.

I don't disagree. I like that they've gone back to a non-glass back. I like even more that it's smaller, lighter, more powerful and let's be honest -- it has an improved, albeit still proprietary, plug. No more trying to plug it in the wrong way? Yes please. Still, these are evolutionary in nature and really only mildly so. It's going to sell like hotcakes, I'm sure. Though why people would want to use hotcakes as a cellphone is outside the scope of this post. And if I weren't already happy with my iPhone 4 (not even a 4S), I would upgrade. I'll take a pass for now and wait awhile. Still, there are some things I would have liked to have seen.


Speaking of an iPhone surviving catastrophes, what about water? I've reviewed the Otterbox case for iPhone 4 but have since quit using it because the rubber shell was beginning to tear near the headphone flap and I decided I was going to try using my iPhone au naturale. So far I haven't had any disasters and I'm doing alright with transporting my iPhone in my pocket but while I would dislike dropping it, I don't think it would cause the same amount of panic as dipping it in liquid would. To that end, would it be possible to use some sort of liquid repelling construction or substance to at least provide a little peace of mind? It would be nice to know my iPhone could be used in the rain, or on a train, and on a bus, with a friend named Gus... ok, I'll stop now.


Right now, you might be wondering about the dock connector. There's been a number of comments about Apple missed a chance to win some points by switching to the ubiquitous micro-USB connector rather than their new proprietary Lightning connector (seriously guys? Thunderbolt and Lightning? really?). If this article is to be believed, there are some solid reasons not to use micro-USB and in fact I think it would be reasonable to offer it as a new standard and to start switching phones to support it. That said, others have commented that while Apple might be justified in using their new connector, they should have offered adapters. That I also disagree with. If you're buying a new iPhone 5, you're getting a new Lightning adapter. If you have devices which work with the old Apple dock connector, in many cases an adapter won't work purely because of the physical layout, not to mention the iPhone 5's new dimensions. So in this case, my request is simple... Apple, offer your new connector up as a new standard, available for the industry to move to. It does seem to have some keen advantages and it would be nice to have everyone on the same page, widening the availability of third party add-ons that work with any phone, as opposed to having to pick one ecosystem or the other.

That's all for me. There are other things that could be mentioned (NFC anyone?) but for me personally, this much and I'd be a very happy camper. That's not to say the iPhone 5 isn't a nice phone as is. Sure it is. But it could just be that much nicer. Tim? Are you taking requests?

Filed under: tech 2 Comments