Jump to content

Recommended Posts

Posted

So for my Airship's/Physics engine mod I've been having issues with the performance. It's almost exclusively caused by collisions among different dimensions.

 

(If you dont know what I mean, then this is what I mean)

[embed=425,349]<iframe width="560" height="315" src="https://www.youtube.com/embed/WTWAMOpxVVY" frameborder="0" allowfullscreen></iframe>[/embed]

 

 

In my attempts to get this working efficiently, I've spent the past few days profiling and fixing bottlenecks; but that all stopped today. I'm not sure whether the JVM hotspot finder is broken, or if I've hit a brick wall here because I have no idea how I'm supposed to handle this bottleneck! There's literally nothing I can do to make this bastard run any faster! (Code and profiling below)

 

ShipOnWorldCollider.class

/* Copyright (C) Alexander Mastrangelo, - All Rights Reserved
* Unauthorized copying of this file, via any medium is strictly prohibited
* Proprietary and confidential
* Written by Alexander Mastrangelo
*/

package ValkyrianWarfare.Collision.Colliders;

import java.util.ArrayList;

import ValkyrianWarfare.ValkyrianWarfareMod;
import ValkyrianWarfare.API.Vector;
import ValkyrianWarfare.API.YVector;
import ValkyrianWarfare.Collision.MovablePolygon;
import ValkyrianWarfare.Collision.Polygon;
import ValkyrianWarfare.Collision.ReusablePhysCollisionObject;
import ValkyrianWarfare.Core.CallRunner;
import ValkyrianWarfare.Entities.Ship;
import ValkyrianWarfare.Math.BigBastardMath;
import ValkyrianWarfare.Math.RotationMatrices;
import ValkyrianWarfare.Physics.PhysicsController;
import ValkyrianWarfare.Region.ShipRegionServer;
import ValkyrianWarfare.Region.WorldUtils.RegionChunkProvider;
import net.minecraft.block.state.IBlockState;
import net.minecraft.init.Blocks;
import net.minecraft.util.AxisAlignedBB;
import net.minecraft.util.BlockPos.MutableBlockPos;
import net.minecraft.util.MathHelper;
import net.minecraft.world.World;
import net.minecraft.world.chunk.Chunk;

/**
* Used to detect collisions between the Ship and the World, and then apply a response
* @author thebest108
*
*/
public final class ShipOnWorldCollider{

public final Ship ship;
//Potential dark matter here? 
public final World worldCollidingWith;
public boolean isColliding = false;
public PhysicsController shipPhys;
private static final Vector defaultAxis = new YVector(0,1D,0);
private static final MutableBlockPos pos = new MutableBlockPos();
private static final AxisAlignedBB zeroAABB = new AxisAlignedBB(0,0,0,1D,1D,1D);
private static final MovablePolygon zeroPoly = new MovablePolygon(zeroAABB);
private static Chunk chunkForPos;
private static final Chunk[][] cachedChunks = new Chunk[16][16];
private static IBlockState iblockstate;
private static final ReusablePolygonCollider polyColReusable = new ReusablePolygonCollider();
private static final Polygon poly = new Polygon(zeroAABB);
private static final Polygon inWorldPoly = new Polygon(zeroAABB);
private static final Vector inBodyVector = new Vector();
private static final Vector responseVector = new Vector();
private static final Vector momentumAtPoint = new Vector();
private static final Vector simpleImpulse = new Vector();
private static final Vector firstCross = new Vector();
private static final Vector secondCross = new Vector();
private static final Vector rotationVec = new Vector();
private static double e = .3D,j;
private double x,y,z;
private static int mnX,mnY,mnZ,mxX,mxY,mxZ,k1,l1,i2,k1shit,l1shift;
private static final ReusablePhysCollisionObject physCol = new ReusablePhysCollisionObject();

//New optimizations past here:
private static final AxisAlignedBB originBB = new AxisAlignedBB(-.5D,-.5D,-.5D,.5D,.5D,.5D);
private static final Polygon originPoly = new Polygon(originBB);
private static final Vector polyOffsetVector = new Vector();
private static final Polygon p = new Polygon(zeroAABB);
private static final Vector mixed = new Vector();
private final QuickCollider quickCols;
private static AxisTest toUse;
public final boolean useFastCalculations;
private final Vector[] axes;
private AxisAlignedBB aabb;
public Chunk chunkIn;
public boolean isSolid;
final RegionChunkProvider provider;

public ShipOnWorldCollider(){
	quickCols = null;
	axes = null;
	ship = null;
	provider = null;
	worldCollidingWith = null;
	isColliding=true;
	useFastCalculations = true;
}

public ShipOnWorldCollider(Ship parent,World worldFor){
	ship = parent;
	worldCollidingWith = worldFor;
	useFastCalculations = true;
	if(parent.doPhysics){
		shipPhys = ((ShipRegionServer)ship.region).physController;
		axes = ship.getSeperatingAxesWithWorld();
		provider = (RegionChunkProvider) ship.region.chunkProvider;
		if(useFastCalculations){
			originPoly.setAABBAndMatrix(originBB, ship.rotationTransform);
			quickCols = new QuickCollider(p, originPoly, axes);
		}else{
			quickCols = null;
		}
	}else{
		axes = null;
		quickCols = null;
		provider = null;
		return;
	}
	if(shouldUpdateCollisionMap()){
		updateCollisionMap();
	}else{
		ship.ticksSinceCollisionUpdate+=(20D*shipPhys.physicsSpeed);
	}
	if(ship.lastCollisionDataPoll.isEmpty()){
		isColliding = false;
		return;
	}
	runExternalLoop();
}

public final void runExternalLoop(){
	for(AxisAlignedBB inWorld:ship.lastCollisionDataPoll){
		if(!useFastCalculations){
			inWorldPoly.setAABBCorners(inWorld);
		}
		aabb = inWorld;
		rotationVec.X=inWorld.minX+.5D;
		rotationVec.Y=inWorld.minY+.5D;
		rotationVec.Z=inWorld.minZ+.5D;
		RotationMatrices.applyTransform(ship.wToLTransform, rotationVec);
		mnX = MathHelper.floor_double(rotationVec.X-.8D);
	   	mxX = MathHelper.floor_double(rotationVec.X+1.8D);
	   	mnY = MathHelper.floor_double(rotationVec.Y-.8D);
	   	mxY = MathHelper.floor_double(rotationVec.Y+1.8D);
	    	mnZ = MathHelper.floor_double(rotationVec.Z-.8D);
	    	mxZ = MathHelper.floor_double(rotationVec.Z+1.8D);
	    	placeHolderBefore();
	}
}

public final void placeHolderBefore(){
	k1=mnX;
   	placeHolder();
	k1++;
	placeHolder();
	k1++;
 	placeHolder();
}

public final void placeHolder(){
	l1 = mnZ;
        	smallerOne();
     		l1++;
        	smallerOne();
        	l1++;
        	smallerOne();
}

public final void smallerOne(){
	k1shit = k1>>4;
    		l1shift = l1>>4;
     	  	 if(k1shit>-17&&k1shit<16&&l1shift>-17&&l1shift<16){
        		k1shit+=16;
        		l1shift+=16;
        		chunkIn = provider.chunksAtCoord[k1shit][l1shift];
        		if(chunkIn!=null){
        			i2 = mnY;
        			runInternalLoop();
        			i2++;
        			runInternalLoop();
        			i2++;
        			runInternalLoop();
        		}
      	 	 }
}

public final void runInternalLoop(){
	isSolid = CallRunner.isBlockSolid(chunkIn,k1, i2, l1);
	if(isSolid){
    			if(useFastCalculations){
    				runOptimizedCollision();
    			}else{
    				runAccurateCollision();
    			}
    		}
}

public final void runOptimizedCollision(){
	x = k1+.5D;
	y = i2+.5D;
	z = l1+.5D;
	polyOffsetVector.X = x * ship.lToWTransform[0] + y * ship.lToWTransform[1] + z * ship.lToWTransform[2] + ship.lToWTransform[3]-aabb.minX;
	polyOffsetVector.Y = x * ship.lToWTransform[4] + y * ship.lToWTransform[5] + z * ship.lToWTransform[6] + ship.lToWTransform[7]-aabb.minY;
	polyOffsetVector.Z = x * ship.lToWTransform[8] + y * ship.lToWTransform[9] + z * ship.lToWTransform[10] + ship.lToWTransform[11]-aabb.minZ;
	quickCols.processOffsetsSingle(polyOffsetVector);
	polyOffsetVector.X += aabb.minX;
	polyOffsetVector.Y += aabb.minY;
	polyOffsetVector.Z += aabb.minZ;
	if(!quickCols.isSeperated){
		toUse = quickCols.axisTests[1];
		//Reusing X here
		x = toUse.penetrationDis;
		if(x>.3D||x<-.3D){
			toUse = quickCols.returnBestAxis();
		}
		toUse.setResponse(responseVector);
		polyOffsetVector.add(originPoly.vertices[toUse.lastContact]);
		BigBastardMath.setInBodyWOFromInWorld(polyOffsetVector, shipPhys.centerOfMass, ship.rotationTransform, ship.wToLTransform,inBodyVector);
		shipPhys.setVectorMomentumAtPoint(inBodyVector, momentumAtPoint);
		handleCollision(inBodyVector,momentumAtPoint,responseVector,toUse.axis);
	}
}

public final void runAccurateCollision(){
	pos.set(k1, i2, l1);
	iblockstate = CallRunner.getStateQuik(chunkIn,k1, i2, l1);
	aabb = iblockstate.getBlock().getCollisionBoundingBox(ship.region, pos, iblockstate); 
	if(aabb!=null){
		poly.setAABBAndMatrix(aabb, ship.lToWTransform);
		polyColReusable.processData(inWorldPoly,poly,axes);
		if(!polyColReusable.seperated){
			physCol.generateCollision(poly, inWorldPoly, defaultAxis);
			if(!physCol.seperated){
				physCol.setResponse(responseVector);
				BigBastardMath.setInBodyWOFromInWorld(physCol.firstContactPoint, shipPhys.centerOfMass, ship.rotationTransform, ship.wToLTransform,inBodyVector);
				shipPhys.setVectorMomentumAtPoint(inBodyVector, momentumAtPoint);
				handleCollision(inBodyVector,momentumAtPoint,responseVector,physCol.axis);
			}
		}
	}
}

public final boolean handleCollision(Vector inBodyWO,Vector velocityAtPoint,Vector offset,Vector axis){
	j = -(e+1D)*velocityAtPoint.dot(axis);
	firstCross.setCross(inBodyWO, axis);
	RotationMatrices.applyTransform3by3(shipPhys.invMoITensor, firstCross);
	secondCross.setCross(firstCross, inBodyWO);
	j = j/((1D/shipPhys.mass)+secondCross.dot(axis));
	simpleImpulse.X = axis.X*j;
	simpleImpulse.Y = axis.Y*j;
	simpleImpulse.Z = axis.Z*j;
	if(simpleImpulse.dot(offset)<0){
		shipPhys.linearMomentum.add(simpleImpulse);
		firstCross.setCross(inBodyWO,simpleImpulse);
		RotationMatrices.applyTransform3by3(shipPhys.invMoITensor,firstCross);
		shipPhys.angularVelocity.add(firstCross);
		return true;
	}
	return false;
}

public void handleFriction(Vector inBodyWO,Vector velocityAtPoint,Vector axis,AxisAlignedBB inWorldBB){
	double llamda = .16D;
	double dot = velocityAtPoint.dot(axis);
	Vector frictionVec = new Vector(velocityAtPoint);
	frictionVec.X-=(dot*axis.X);
	frictionVec.Y-=(dot*axis.Y);
	frictionVec.Z-=(dot*axis.Z);
//		frictionVec.multiply(llamda);
//		double minMagnitude = .025D;
	double moddedLlamda = Math.pow(llamda,shipPhys.physicsSpeed*ValkyrianWarfareMod.serverShipManager.getIterationsForWorld(ship.worldObj)/ValkyrianWarfareMod.serverShipManager.getSpeedForWorld(ship.worldObj));
//		if(frictionVec.lengthSq()<Math.pow(minMagnitude,shipPhys.physicsSpeed*ValkyrianWarfareMod.serverShipManager.getIterationsForWorld(ship.worldObj)/ValkyrianWarfareMod.serverShipManager.getSpeedForWorld(ship.worldObj))){
//			moddedLlamda = 1D;
//		}
	frictionVec.multiply(-moddedLlamda);
	shipPhys.linearMomentum.add(frictionVec);
	shipPhys.angularVelocity.add(RotationMatrices.get3by3TransformedVec(shipPhys.invMoITensor, inBodyWO.cross(frictionVec)));
}

public final void updateCollisionMap(){
	ship.lastCollisionPollAABB = ship.getEntityBoundingBox().expand(1D, 1D, 1D);
	ship.lastCollisionDataPoll = getCollidingWorldBBs(worldCollidingWith,ship.lastCollisionPollAABB);
//		BigBastardMath.mergeAABBList(ship.lastCollisionDataPoll);
//		ship.lastCollisionDataPoll.trimToSize();
	ship.ticksSinceCollisionUpdate = 0D;
}

public final boolean shouldUpdateCollisionMap(){
	if(ship.ticksSinceCollisionUpdate>1.25D||ship.lastCollisionDataPoll == null){
		return true;
	}
	return false;
}

public final static ArrayList<AxisAlignedBB> getCollidingWorldBBs(World world,AxisAlignedBB bb){
	ArrayList arraylist = new ArrayList<AxisAlignedBB>((int) Math.ceil((bb.maxX-bb.minX)*(bb.maxY-bb.minY)*(bb.maxZ-bb.minZ)*1.2D));
    int mnX = MathHelper.floor_double(bb.minX);
    int mxX = MathHelper.floor_double(bb.maxX + 1.0D);
    int mnY = MathHelper.floor_double(bb.minY);
    int mxY = MathHelper.floor_double(bb.maxY + 1.0D);
    int mnZ = MathHelper.floor_double(bb.minZ);
    int mxZ = MathHelper.floor_double(bb.maxZ + 1.0D);

    int minChunkX = mnX >> 4;
	int minChunkZ = mnZ >> 4;
    int maxChunkX = mxX >> 4;
    int maxChunkZ = mxZ >> 4;
    for(int x = minChunkX;x <= maxChunkX;x++){
    	for(int z = minChunkZ;z <= maxChunkZ;z++){
        	cachedChunks[x-minChunkX][z-minChunkZ] = world.getChunkFromChunkCoords(x, z);
	    }
    }
    for(int k1 = mnX; k1 < mxX; ++k1){
    	for(int l1 = mnZ; l1 < mxZ; ++l1){
    		chunkForPos = cachedChunks[(k1>>4)-minChunkX][(l1>>4)-minChunkZ];
            if(!chunkForPos.isEmpty()){
            	for (int i2 = mnY - 1; i2 < mxY; ++i2){
            		pos.set(k1, i2, l1);
            		iblockstate = chunkForPos.getBlockState(pos);
	                if(iblockstate.getBlock()!=Blocks.air){
	                	iblockstate.getBlock().addCollisionBoxesToList(world, pos, iblockstate, bb, arraylist, null);
	                }
                }
            }
        }
    }
    arraylist.trimToSize();
    return arraylist;
}

}

 

After a bit of simple profiling I came out with this

BToMYnu.png

 

You'll notice that the PlaceHolder() method with an 80% self time is made up this complicated set

public final void placeHolder(){
l1 = mnZ;
        smallerOne();
        l1++;
        smallerOne();
        l1++;
        smallerOne();
}

 

The fact that 3 invocations and additions can have a self-time of 80% compared to everything else appalls me. I'm asking what the hell is this? Is it a bug with the profiler, is the structure of the methods bad, or are these invocations truly the most complicated things in the code?

"you seem to be THE best modder I've seen imo."

~spynathan

 

ლ(́◉◞౪◟◉‵ლ

Posted

Damn,I'm really interested in knowing how each line of code affects the performance, and(until recently) I've been trying to figure this out by separating pieces of code into different methods.

 

Is there a way for me to get an accurate line-by-line performance analysis of a method? I want this biker to be peddling as fast as possible before I move onto multi-threading.

"you seem to be THE best modder I've seen imo."

~spynathan

 

ლ(́◉◞౪◟◉‵ლ

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Unfortunately, your content contains terms that we do not allow. Please edit your content to remove the highlighted words below.
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Announcements



×
×
  • Create New...

Important Information

By using this site, you agree to our Terms of Use.