My rigid body models are dropping through the floor. I am experimenting with Libgdx. When models fall on the ground created from a box they stop moving, but when I replace the ground with a model created from blender the objects fall through. I am trying to build a road track. Here is the code and other examples I tried I keep getting objects that fall through the modeled ground. I'm using blender for the modelling.
public BulletSample(ThrustCopter thrustCopter) {
super(thrustCopter);
// Create ModelBatch that will render all models using a camera
modelBatch = new ModelBatch(new DefaultShaderProvider());
// Create a camera and point it to our model
camera = new PerspectiveCamera(70, Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
camera.position.set(10f, 10f, 12f);
camera.lookAt(0, 0, 0);
camera.near = 0.1f;
camera.far = 300f;
camera.update();
// Create the generic camera input controller to make the app interactive
cameraController = new CameraInputController(camera);
Gdx.input.setInputProcessor(cameraController);
// Set up environment with simple lighting
environment = new Environment();
environment.set(new ColorAttribute(ColorAttribute.AmbientLight, 0.4f, 0.4f, 0.4f, 1f));
// environment.set(new ColorAttribute(ColorAttribute.Fog, 0.13f, 0.13f, 0.13f, 1f));
environment.add(new DirectionalLight().set(0.8f, 0.8f, 0.8f, -0.8f, 0.3f, -1f));
shadowLight = new DirectionalShadowLight(1024, 1024, 60, 60, 1f, 300);
shadowLight.set(0.8f, 0.8f, 0.8f, -1f, -.8f, -.2f);
environment.add(shadowLight);
environment.shadowMap = shadowLight;
shadowBatch = new ModelBatch(new DepthShaderProvider());
Bullet.init();
collisionConfiguration = new btDefaultCollisionConfiguration();
dispatcher = new btCollisionDispatcher(collisionConfiguration);
broadphase = new btDbvtBroadphase();
solver = new btSequentialImpulseConstraintSolver();
world = new btDiscreteDynamicsWorld(dispatcher, broadphase, solver, collisionConfiguration);
world.setGravity(new Vector3(0, -9.81f, 1f));
ModelBuilder modelBuilder = new ModelBuilder();
/* Model ground = modelBuilder.createBox(40f, 2f, 40f,
new Material(ColorAttribute.createDiffuse(Color.GREEN)),
Usage.Position | Usage.Normal);
groundInstance=new ModelInstance(ground);
btCollisionShape groundshape = new btBoxShape(new Vector3(20, 1f, 20));
shapes.add(groundshape);
btRigidBody body = new btRigidBody(0,null,groundshape);
bodies.add(body);
world.addRigidBody(body); */
// testing rigid body ground with model from blender
game.manager.load("gameAssets/roadtest1.g3db", Model.class);
game.manager.finishLoading();
Model roadmodel = game.manager.get("gameAssets/roadtest1.g3db", Model.class);
roadInstance = new ModelInstance(roadmodel);
//instances.add(road);
btCollisionShape roadshape = createConvexHullShape(roadmodel, true);
shapes.add(roadshape);
btRigidBody body = new btRigidBody(0, null, roadshape);
world.addRigidBody(body);
Vector3 position=new Vector3();
for(int i=0;i<10;i++){
Model box = modelBuilder.createBox(1f, 1f, 1f,
new Material(ColorAttribute.createDiffuse(Color.BLUE)),
Usage.Position | Usage.Normal);
ModelInstance boxInstance=new ModelInstance(box);
instances.add(boxInstance);
models.add(box);
if(i<5){
position.set(-1, i+1, 0);
}else{
position.set(1, i-4, 0);
}
boxInstance.transform.setToTranslation(position);
btDefaultMotionState motionState = new btDefaultMotionState(boxInstance.transform);
motionState.setWorldTransform(boxInstance.transform.trn(0, 0, 0));
motionStates.add(motionState);
btCollisionShape boxshape = new btBoxShape(new Vector3(0.5f, 0.5f, 0.5f));
shapes.add(boxshape);
btRigidBody boxbody = new btRigidBody(1, motionState, boxshape);
bodies.add(boxbody);
world.addRigidBody(boxbody);
}
//Loading model
game.manager.load("planeanim1.g3db", Model.class);
game.manager.finishLoading();
Model model = game.manager.get("planeanim1.g3db", Model.class);
ModelInstance plane = new ModelInstance(model);
instances.add(plane);
plane.transform.setToRotation(Vector3.Y, 180);
plane.transform.trn(0, 7, 10);
btDefaultMotionState motionState = new btDefaultMotionState(plane.transform);
motionState.setWorldTransform(plane.transform.trn(0, 0, 0));
// motionState.setWorldTransform(plane.transform.rotate(0, 0, .5f,45));
motionStates.add(motionState);
btCollisionShape planeshape = createConvexHullShape(model,true);
shapes.add(planeshape);
btRigidBody planebody = new btRigidBody(5, motionState, planeshape);
bodies.add(planebody);
world.addRigidBody(planebody);
planebody.userData="plane";
planebody.applyCentralImpulse(new Vector3(0,0,-65));
// You use an AnimationController to control animations. Each control is tied to the model instance
controller = new AnimationController(plane);
// Pick the current animation by name
controller.setAnimation("Scene",-1);
contactListener = new MyContactListener();
}
public static btConvexHullShape createConvexHullShape (final Model model, boolean optimize) {
final Mesh mesh = model.meshes.get(0);
final btConvexHullShape shape = new btConvexHullShape(mesh.getVerticesBuffer(), mesh.getNumVertices(), mesh.getVertexSize());
if (!optimize) return shape;
// now optimize the shape
final btShapeHull hull = new btShapeHull(shape);
hull.buildHull(shape.getMargin());
final btConvexHullShape result = new btConvexHullShape(hull);
// delete the temporary shape
shape.dispose();
hull.dispose();
return result;
}
#Override
public void dispose() {
groundInstance.model.dispose();
instances.clear();
modelBatch.dispose();
for (Model model : models)
model.dispose();
for (btRigidBody body : bodies) {
body.dispose();
}
for (btMotionState motion : motionStates)
motion.dispose();
for (btCollisionShape shape : shapes)
shape.dispose();
world.dispose();
collisionConfiguration.dispose();
dispatcher.dispose();
broadphase.dispose();
solver.dispose();
contactListener.dispose();
shadowBatch.dispose();
shadowLight.dispose();
}
#Override
public void render(float delta) {
Gdx.gl.glViewport(0, 0, Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT | GL20.GL_DEPTH_BUFFER_BIT);
Gdx.gl.glClearColor(0.13f, 0.13f, 0.13f, 1);
if(started){
world.stepSimulation(Gdx.graphics.getDeltaTime(), 5);
for (int i = 0; i < motionStates.size; i++) {
motionStates.get(i).getWorldTransform(instances.get(i).transform);
}
}else{
if(Gdx.input.isTouched()){
started=true;
System.out.println(instances.size);
if(Gdx.input.justTouched())instances.get(10).transform.rotate(0,0.2f,0,25);
}
}
// Respond to user events and update the camera
cameraController.update();
controller.update(delta);
shadowLight.begin(Vector3.Zero, camera.direction);
shadowBatch.begin(shadowLight.getCamera());
shadowBatch.render(instances);
shadowBatch.end();
shadowLight.end();
// Draw all model instances using the camera
modelBatch.begin(camera);
//modelBatch.render(groundInstance, environment);
modelBatch.render(roadInstance, environment);
modelBatch.render(instances, environment);
modelBatch.end();
super.render(delta);
}
Related
I'm trying to add shadows to the bottom of my model but the shadow isn't showing at the bottom. Can anyone help? This is what I tried using the deprecated DirectionalShadowLight:
environment = new Environment();
environment.set(new ColorAttribute(ColorAttribute.AmbientLight, .4f, .4f, .4f, 1f));
environment.add(new DirectionalLight().set(0.8f, 0.8f, 0.8f, -1f, -0.8f, -0.2f));
shadowLight = new DirectionalShadowLight(1024, 1024, 60, 60, 1f, 300);
shadowLight.set(0.8f, 0.8f, 0.8f, -1f, -.8f, -.2f);
environment.add(shadowLight);
environment.shadowMap = shadowLight;
shadowBatch = new ModelBatch(new DepthShaderProvider());
camera = new PerspectiveCamera(67, screenWidth, screenHeight);
camera.position.set(0, 6, 2);
camera.direction.set(0, 0, -4).sub(camera.position).nor();
camera.near = 1;
camera.far = 300;
camera.update();
private void renderSanta(GL20 gl) {
gl.glEnable(GL20.GL_DEPTH_TEST);
gl.glEnable(GL20.GL_CULL_FACE);
// /****************
shadowLight.begin(Vector3.Zero, camera.direction);
shadowBatch.begin(shadowLight.getCamera());
shadowBatch.render(santaModel);
shadowBatch.end();
shadowLight.end();
// ********************/
modelBatch.begin(camera);
modelBatch.render(santaModel, environment);
modelBatch.end();
gl.glDisable(GL20.GL_CULL_FACE);
gl.glDisable(GL20.GL_DEPTH_TEST);
}
private void renderBackground() {
viewMatrix.setToOrtho2D(0, 0, 480, 800);
spriteBatch.setProjectionMatrix(viewMatrix);
spriteBatch.begin();
spriteBatch.enableBlending();
spriteBatch.setColor(Color.WHITE);
spriteBatch.draw(background, 0, 0, 480, 800);
spriteBatch.draw(hammock, 0, 800 - 500 - 95, 182, 95);
spriteBatch.disableBlending();
spriteBatch.end();
}
#Override
public void draw(float delta) {
GL20 gl = Gdx.gl;
gl.glClear(GL20.GL_COLOR_BUFFER_BIT | GL20.GL_DEPTH_BUFFER_BIT);
renderBackground();
renderSanta(gl);
}
EDIT:
So like Xoppa commented, I added a "plane" under the santa model but still the shadow wasn't shown:
ModelBuilder modelBuilder = new ModelBuilder();
modelBuilder.begin();
MeshPartBuilder mpb = modelBuilder.part("parts", GL20.GL_TRIANGLES, VertexAttributes.Usage.Position | VertexAttributes.Usage.Normal | VertexAttributes.Usage.ColorUnpacked,
new Material(ColorAttribute.createDiffuse(Color.WHITE)));
mpb.box(0, -1.5f, 0, 7, 1, 7);
mpb.setColor(1f, 0f, 1f, 1f);
model = modelBuilder.end();
instance = new ModelInstance(model);
instance.transform.setTranslation(0, -5, -3);
public void renderModel() {
shadowLight.begin(Vector3.Zero, camera.direction);
shadowBatch.begin(shadowLight.getCamera());
shadowBatch.render(instance);
shadowBatch.end();
shadowLight.end();
modelBatch.begin(camera);
modelBatch.render(instance, environment);
modelBatch.end();
}
#Override
public void draw(float delta) {
GL20 gl = Gdx.gl;
gl.glClear(GL20.GL_COLOR_BUFFER_BIT | GL20.GL_DEPTH_BUFFER_BIT);
renderBackground();
renderModel();
renderSanta(gl);
}
Updated output still now showing shadows
Edit 2: Thanks to Xoppa I was able to make the shadow appear:
I'm new in Libgdx and I'm developing 3d game in libgdx, so I want to create first person system, I created a class -playercontroller- to move the player and then make the camera move with player and it work
But I want the player to move in the camera direction, so when I rotate the camera , the player walk in the new direction.
This my code:-
#Override
public void create() {
// load enviroment
environment = new Environment();
environment.set(new ColorAttribute(ColorAttribute.AmbientLight, 0.4f, 0.4f, 0.4f, 1f));
environment.add(new DirectionalLight().set(0.8f, 0.8f, 0.8f, -1f, -0.8f, -0.2f));
// setup camera
cam = new PerspectiveCamera(67, Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
cam.position.set(10f, 10f, 10f);
cam.lookAt(0, 0, 0);
cam.near = 0.001f;
cam.far = 3000f;
cam.update();
// setup controller for camera
camController = new CameraInputController(cam);
Gdx.input.setInputProcessor(camController);
// load the models
assets = new AssetManager();
assets.load("ground_stairs.g3db", Model.class);
assets.load("gathering_node.g3db", Model.class);
loading = true;
modelBatch = new ModelBatch();
// setup bulletphysics
Bullet.init();
collisionConfig = new btDefaultCollisionConfiguration();
dispatcher = new btCollisionDispatcher(collisionConfig);
broadphase = new btDbvtBroadphase();
constraintSolver = new btSequentialImpulseConstraintSolver();
dynamicsWorld = new btDiscreteDynamicsWorld(dispatcher, broadphase, constraintSolver, collisionConfig);
dynamicsWorld.setGravity(new Vector3(0, -10f, 0));
contactListener = new MyContactListener();
loadPlayer();
}
private void loadPlayer() {
// setup player/camera movement
pc = new PlayerController(instances,cam,dynamicsWorld);
}
private void doneLoading() {
loading = false;
onDoneLoadingStatic("ground_stairs.g3db", GROUND);
onDoneLoadingStatic("gathering_node.g3db", GATHERING_NODE);
gatheringNode = instances.get(instances.size() - 1);
}
public btRigidBody onDoneLoadingStatic(String fileName, int id) {
Model model = assets.get(fileName, Model.class);
ModelInstance instance = new ModelInstance(model);
instances.add(instance);
btBvhTriangleMeshShape shape = new btBvhTriangleMeshShape(instance.model.meshParts);
btRigidBody body = new btRigidBody(0, null, shape, new Vector3(0, 0, 0));
body.proceedToTransform(instance.transform);
// set id to find with collision detection
body.setUserValue(id);
dynamicsWorld.addRigidBody(body);
return body;
}
#Override
public void render() {
if (loading && assets.update()) {
doneLoading();
}
camController.update();
pc.update();
final float delta = Math.min(1f / 30f, Gdx.graphics.getDeltaTime());
dynamicsWorld.stepSimulation(delta, 5, 1f / 60f);
Gdx.gl20.glClearColor(0, 0.5f, 1, 1);
Gdx.gl20.glViewport(0, 0, Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
Gdx.gl20.glClear(GL20.GL_COLOR_BUFFER_BIT | GL20.GL_DEPTH_BUFFER_BIT);
modelBatch.begin(cam);
modelBatch.render(instances, environment);
modelBatch.end();
}
#Override
public void dispose() {
modelBatch.dispose();
}
private class MyContactListener extends ContactListener {
#Override
public void onContactStarted(int userValue0, int userValue1) {
if (userValue0 == PLAYER && userValue1 == GATHERING_NODE) {
((ColorAttribute) gatheringNode.materials.first().get(ColorAttribute.Diffuse)).color.set(Color.RED);
}
if (userValue0 == PLAYER && userValue1 == GROUND) {
PlayerController.canJump = true;
}
}
#Override
public void onContactEnded(int userValue0, int userValue1) {
if (userValue0 == PLAYER && userValue1 == GATHERING_NODE) {
((ColorAttribute) gatheringNode.materials.first().get(ColorAttribute.Diffuse)).color.set(Color.BLUE);
}
}
}
}
PlayerController
public PlayerController(List<ModelInstance> instances,PerspectiveCamera cam,btDynamicsWorld dynamicWorld) {
this.instances = instances;
this.cam = cam;
this.dynamicsWorld = dynamicWorld;
player = new ModelInstance(new ModelBuilder()
.createCapsule(0.25f, 3, 10, new Material(ColorAttribute.createAmbient(Color.BLACK)), Usage.Normal | Usage.Position)
);
player.transform.translate(5, 7, 0);
instances.add(player);
// load player rigid body
btCapsuleShape playerShape = new btCapsuleShape(0.25f, 2.5f);
float mass = 10;
Vector3 localInertia = new Vector3();
playerShape.calculateLocalInertia(mass, localInertia);
playerBody = new btRigidBody(mass, null, playerShape, localInertia);
playerBody.proceedToTransform(player.transform);
playerBody.setCollisionFlags(playerBody.getCollisionFlags() | btCollisionObject.CollisionFlags.CF_CUSTOM_MATERIAL_CALLBACK);
// set id to find with collision detection
playerBody.setUserValue(PLAYER);
dynamicsWorld.addRigidBody(playerBody);
}
public void update() {
// make sure to activate the player body so bullet doesnt put it to sleep
playerBody.activate();
// prevent the capsule from falling over
playerBody.setAngularFactor(new Vector3(0, 0, 0));
playerBody.getWorldTransform(player.transform);
Vector3 velocity = new Vector3(0, playerBody.getLinearVelocity().y, 0);
velocity.x = xposition;
velocity.z = yposition;
cam.position.set(player.transform.getTranslation(new Vector3()));
cam.position.y=5;
cam.rotate(Vector3.Y,angleX);
cam.rotate(cam.direction.cpy().crs(Vector3.Y),-angleY);
playerBody.setLinearVelocity(velocity);
cam.update();
}
}
The code is work without problem and I can walk forward and backward, but when I rotate the the direction doesn't work, so how can I make it work?
public float posX, posY, posZ;
private float move = 0.02f;
public void moveForward(float moveSpeed) {
// normal speed moveForward(3.9);
//call moveForward(0); to stop moving
moveRCam(-moveSpeed, 0);
}
public void moveBackward(float moveSpeed) {
moveRCam(moveSpeed, 0);
}
private void moveRCam(float moveSpeed, float st) {
float speed = moveSpeed * move;
float strVel = st * move;
float angleY = (cam.rotation.y / 180 * 3.141592654f);
posX += speed*(Math.sin(angleY));
posZ -= speed*(Math.cos(angleY));
angleY += (3.141592654f/2);
posX -= strVel*(Math.sin(angleY));
posZ += strVel*(Math.cos(angleY));
cam.position.x = posX;
cam.position.y = posY;
cam.position.z = posZ;
}
I have tried to load a model and display it in my window for about 2 hours, but now i surrender. I tried about 10 different models that i downloaded from https://sketchfab.com, but none is displayed, i just get a black window. I searched for help in the web another 20 minutes now but as i am not very experienced in libgdx, i cant adapt the "solutions" i found to my programm. I hope you can help me!
Another question i have is, that if there was any way to use models with more indexes than the short value.
model i used: https://sketchfab.com/models/042201dac61041fabb88f483368daa3f
my code:
public class Game extends ApplicationAdapter
{
private PerspectiveCamera cam;
private CameraInputController camController;
private ModelBatch modelBatch;
private AssetManager assets;
private Array<ModelInstance> instances = new Array<ModelInstance>();
private Environment environment;
private boolean loading;
#Override
public void create () {
modelBatch = new ModelBatch();
environment = new Environment();
environment.set(new ColorAttribute(ColorAttribute.AmbientLight, 0.4f, 0.4f, 0.4f, 1f));
environment.add(new PointLight().set(0.8f, 0.8f, 0.8f, -1f, -0.8f, -0.2f, 1f));
cam = new PerspectiveCamera(67, Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
cam.position.set(1f, 1f, 1f);
cam.lookAt(0,0,0);
cam.near = 0.1f;
cam.far = 300f;
cam.update();
camController = new CameraInputController(cam);
Gdx.input.setInputProcessor(camController);
assets = new AssetManager();
assets.load("basic.g3db", Model.class);
loading = true;
}
private void doneLoading() {
Model ship = assets.get("basic.g3db", Model.class);
ModelInstance shipInstance = new ModelInstance(ship);
instances.add(shipInstance);
loading = false;
}
#Override
public void render () {
if (loading && assets.update())
doneLoading();
camController.update();
Gdx.gl20.glViewport(0, 0, Gdx.graphics.getWidth(),
Gdx.graphics.getHeight());
Gdx.gl20.glClear(GL20.GL_COLOR_BUFFER_BIT | GL20.GL_DEPTH_BUFFER_BIT);
modelBatch.begin(cam);
modelBatch.render(instances, environment);
modelBatch.end();
}
#Override
public void dispose () {
modelBatch.dispose();
instances.clear();
assets.dispose();
}
public void resume () {
}
public void resize (int width, int height) {
}
public void pause () {
}
}
I try to implement some post-processing, so I need to use FrameBuffer to collect entire picture and then post-process it with shader. I've found some examples of how to work with FrameBuffer in LibGDX(Rendering a 3D model to texture in LibGDX), but it doesn't work. I get black screen. My code snippet:
#Override
public void show() {
modelBatch = new ModelBatch();
environment = new Environment();
environment.set(new ColorAttribute(ColorAttribute.AmbientLight, 1f, 1f, 0f, 1f));
environment.add(new DirectionalLight().set(1f, 0.0f, 0.0f, 0f, -2f, 0f));
cam = new PerspectiveCamera(67, Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
cam.position.set(1f, 1f, 1f);
cam.lookAt(0,0,0);
cam.near = 0.1f;
cam.far = 300f;
cam.update();
com.badlogic.gdx.graphics.g3d.loader.ObjLoader loader =new ObjLoader();
model = loader.loadModel(Gdx.files.internal("cube/cube.obj"));
instance = new ModelInstance(model);
fbo = new FrameBuffer(Format.RGBA8888, Gdx.graphics.getWidth(), Gdx.graphics.getHeight(), true);
sb= new SpriteBatch();
camController = new CameraInputController(cam);
Gdx.input.setInputProcessor(camController);
}
#Override
public void render(float delta) {
fbo.begin();
camController.update();
Gdx.gl.glViewport(0, 0, Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
Gdx.gl.glClearColor(0, 0, 0, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT | GL20.GL_DEPTH_BUFFER_BIT);
modelBatch.begin(cam);
modelBatch.render(instance, environment);
modelBatch.end();
fbo.end();
//getting texture
tex = fbo.getColorBufferTexture();
//render texture
spriteBatch.begin();
spriteBatch.draw(tex,0,0, Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
spriteBatch.end();
}
What do I wrong?
UPDATE
I got another problem. Look at the pictures bellow:
I see this hidden fragment of landscape. I use
fb = new FrameBuffer(Format.RGBA8888, Gdx.graphics.getWidth(), Gdx.graphics.getHeight(), false);
But when I try to switch on the depth of FrameBuffer, it shows me black screen again:
fb = new FrameBuffer(Format.RGBA8888, Gdx.graphics.getWidth(), Gdx.graphics.getHeight(), true);
How to get rid of showing this fragment of landscape and why FrameBuffer doesn't work with depth parameter?
You can use ScreenUtils functions to get framebuffer/
http://libgdx.badlogicgames.com/nightlies/docs/api/com/badlogic/gdx/utils/ScreenUtils.html
ScreenUtils.getFrameBufferPixels(int x, int y, int w, int h, boolean flipY)
I am playing with libgdx for making yet another physics game :) and I have found something weird. Namely I use SpriteBatch for rendering images at the same time with Box2DDebugRenderer for debuging.
But when the physics acts, they appear to be misplaced. I wrote:
public class Canon implements ApplicationListener {
private OrthographicCamera camera;
private Box2DDebugRenderer debugRenderer;
/...
public void create() {
camera = new OrthographicCamera(CAMERA_WIDTH, CAMERA_HEIGHT);
world = new World(new Vector2(0f, -9.8f), true);
camera.position.set(CAMERA_WIDTH/2, CAMERA_HEIGHT/2, 0f);
camera.update();
debugRenderer = new Box2DDebugRenderer();
spriteBatch = new SpriteBatch();
//Create a canon. A rectangle :)
bd = new BodyDef();
fd = new FixtureDef(); fd.density = 1;
PolygonShape ps = new PolygonShape();
// Cannon
bd.type = BodyDef.BodyType.StaticBody;
bd.position.set(new Vector2(8, 5));
ps.setAsBox(5f, 1f);
cannonBody = world.createBody(bd);
fd.shape = ps;
cannonBody.createFixture(fd);
}
#Override
public void render() {
Gdx.gl.glClearColor(0, 0, 0, 1);
Gdx.gl.glClear(GL10.GL_COLOR_BUFFER_BIT);
debugRenderer.render(world, camera.combined);
world.step(BOX_STEP, BOX_VELOCITY_ITERATIONS, BOX_POSITION_ITERATIONS);
spriteBatch.begin();
Sprite s = (Sprite)targetBody1.getUserData();
spriteBatch.draw(s.getTexture(),
(targetBody1.getPosition().x - bodyWidth/2)*ppuX, (targetBody1.getPosition().y - bodyheight/2)*ppuY,
0f, 0f, bodyWidth*ppuX, bodyheight*ppuY, 1f, 1f, radToGrad*targetBody1.getAngle(), 0, 0, s.getTexture().getWidth(), s.getTexture().getHeight(), false, false);
spriteBatch.end();
}
}
And here's how it looks thereafter
Any ideas?
Thanks!
I found it. This is due to the fact rotations in OpenGL are done around the bottom left corner, whereas rotations in Box2D are done around mass center's body.
Rotating the texture around mass center body gives right physics/texture behavior.