Show Posts

This section allows you to view all posts made by this member. Note that you can only see posts made in areas you currently have access to.


Messages - HammerNL

Pages: [1]
1
Support / Re: setAdditionalColor
« on: December 02, 2013, 10:48:30 am »
Hi Egon,

I read in this thread that additional color is multiplied with the existing color. That would explain why my black object seems to ignore the additional color. However, setting an additional color of BLACK on a colored object, does not result in a BLACK object. So it's not a simple multiplication.

Also, your documentation states the color is added:

public void setAdditionalColor(RGBColor col)
Sets the additional color for this object. This color will be added to the regular color of each vertex (given by ambient, diffuse and specular lighting).

So I'm a bit confused here: Now how does additionalColor work exactly?

Tnx

2
Support / Re: How to draw a simple 2D line onto my jPCT Framebuffer?
« on: September 11, 2013, 09:46:25 am »
Hi Egon,

YES IT'S FIXED! Both in the HelloWorld app as in my game.
I've got myself a working fuelmeter :-)

Tanx a lot!

3
Support / Re: How to draw a simple 2D line onto my jPCT Framebuffer?
« on: September 10, 2013, 12:28:36 pm »
I'm using the latest I could find: 1.27

4
Support / Re: How to draw a simple 2D line onto my jPCT Framebuffer?
« on: September 10, 2013, 10:02:40 am »
Hi Egon,

Yes of course: Here it is:

Code: [Select]
package com.example.line2don3d;

import java.lang.reflect.Field;

import javax.microedition.khronos.egl.EGL10;
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.egl.EGLDisplay;
import javax.microedition.khronos.opengles.GL10;

import android.app.Activity;
import android.opengl.GLSurfaceView;
import android.os.Bundle;
import android.view.MotionEvent;

import com.threed.jpct.*;
import com.threed.jpct.util.MemoryHelper;

public class HelloWorld extends Activity {

  public final static float PI = (float)Math.PI;
  public final static SimpleVector XAXIS = new SimpleVector(1,0,0);
  public final static SimpleVector YAXIS = new SimpleVector(0,1,0);
  public final static SimpleVector ZAXIS = new SimpleVector(0,0,1);
  public final static float DEGtoRAD = PI/180;

  // Used to handle pause and resume...
  private static HelloWorld master = null;

  private GLSurfaceView mGLView;
  private MyRenderer renderer = null;
  private FrameBuffer fb = null;
  private World world = null;
  private World meterWorld = null;
  private RGBColor back = new RGBColor(0xaa,0xaa,0xaa);

  private float touchTurn = 0;
  private float touchTurnUp = 0;

  private float xpos = -1;
  private float ypos = -1;

  private Camera cam = null;
  private float camX = 100;
  private float camY = 0;
  private Camera mCam = null;
  private float mCamX = 10;
  private float mCamY = 0;

  private Polyline fuelLine = null;
  private Texture fuelMeter = null;

  protected void onCreate(Bundle savedInstanceState) {

    Logger.log("onCreate");

    if (master != null) {
      copy(master);
    }

    super.onCreate(savedInstanceState);
    mGLView = new GLSurfaceView(getApplication());

    mGLView.setEGLConfigChooser(new GLSurfaceView.EGLConfigChooser() {
      public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display) {
        // Ensure that we get a 16bit framebuffer. Otherwise, we'll fall
        // back to Pixelflinger on some device (read: Samsung I7500)
        int[] attributes = new int[] { EGL10.EGL_DEPTH_SIZE, 16, EGL10.EGL_NONE };
        EGLConfig[] configs = new EGLConfig[1];
        int[] result = new int[1];
        egl.eglChooseConfig(display, attributes, configs, 1, result);
        return configs[0];
      }
    });

    renderer = new MyRenderer();
    mGLView.setRenderer(renderer);
    setContentView(mGLView);
  }

  @Override
  protected void onPause() {
    super.onPause();
    mGLView.onPause();
  }

  @Override
  protected void onResume() {
    super.onResume();
    mGLView.onResume();
  }

  @Override
  protected void onStop() {
    super.onStop();
  }

  private void copy(Object src) {
    try {
      Logger.log("Copying data from master Activity!");
      Field[] fs = src.getClass().getDeclaredFields();
      for (Field f : fs) {
        f.setAccessible(true);
        f.set(this, f.get(src));
      }
    } catch (Exception e) {
      throw new RuntimeException(e);
    }
  }

  public boolean onTouchEvent(MotionEvent me) {

    if (me.getAction() == MotionEvent.ACTION_DOWN) {
      xpos = me.getX();
      ypos = me.getY();
      return true;
    }

    if (me.getAction() == MotionEvent.ACTION_UP) {
      xpos = -1;
      ypos = -1;
      touchTurn = 0;
      touchTurnUp = 0;
      return true;
    }

    if (me.getAction() == MotionEvent.ACTION_MOVE) {
      float xd = me.getX() - xpos;
      float yd = me.getY() - ypos;

      xpos = me.getX();
      ypos = me.getY();

      touchTurn = xd / -100f;
      touchTurnUp = yd / -100f;
      return true;
    }

    try {
      Thread.sleep(15);
    } catch (Exception e) {
      // No need for this...
    }

    return super.onTouchEvent(me);
  }

  protected boolean isFullscreenOpaque() {
    return true;
  }

  class MyRenderer implements GLSurfaceView.Renderer {

    public MyRenderer() {
    }

    public void onSurfaceChanged(GL10 gl, int w, int h) {
      if (fb != null) {
        fb.dispose();
      }
      fb = new FrameBuffer(gl, w, h);

      if (master == null) {

        world = new World();
        world.setAmbientLight(100, 100, 100);

        Light sun = new Light(world);
        sun.setIntensity(255,255,255);
        sun.setPosition(new SimpleVector(-100,0,50));

        cam = world.getCamera();
        cam.setPosition(camX,camY,0);
        cam.setOrientation(new SimpleVector(-1,0,0),ZAXIS);

        Object3D cube = Primitives.getCube(10);
        cube.setAdditionalColor(0xff,0xff,0);
        cube.setOrigin(SimpleVector.ORIGIN);
        cube.build();
        world.addObject(cube);

        MemoryHelper.compact();
        world.compileAllObjects();

        meterWorld = new World();
        meterWorld.setAmbientLight(100, 100, 100);

        mCam = meterWorld.getCamera();
        mCam.setPosition(mCamX,mCamY,0);
        mCam.setOrientation(new SimpleVector(-1,0,0),ZAXIS);

        fuelLine = new Polyline(new SimpleVector[] { new SimpleVector(-20,0,20), new SimpleVector(20,20,20) }, RGBColor.RED);
        fuelLine.setWidth(5);
        meterWorld.addPolyline(fuelLine);

        meterWorld.compileAllObjects();

        fuelMeter = new Texture(getResources().openRawResource(R.raw.fuelmeter_256),true);
        fuelMeter.setMipmap(false);

        if (master == null) {
          Logger.log("Saving master Activity!");
          master = HelloWorld.this;
        }
      }
    }

    public void onSurfaceCreated(GL10 gl, EGLConfig config) {
    }

    private float fuel = 0;

    public void onDrawFrame(GL10 gl) {
      if (touchTurn != 0)
      {
        camY += touchTurn*10;
        cam.setPosition(camX,camY,0);
        cam.setOrientation(new SimpleVector(-1,0,0),ZAXIS);
        touchTurn = 0;
      }

      if (touchTurnUp != 0)
      {
        camX += touchTurnUp*10;
        cam.setPosition(camX,camY,0);
        cam.setOrientation(new SimpleVector(-1,0,0),ZAXIS);
        touchTurnUp = 0;
      }

      fuel += 0.01;

      float angle = (-60+fuel*120)*DEGtoRAD;
      int x = 200;
      int y = 200;
      float r = 100;

      SimpleVector v1 = Interact2D.reproject2D3DWS(mCam,fb,x,y);
      v1.add(new SimpleVector(mCamX,mCamY,0));
      SimpleVector v2 = Interact2D.reproject2D3DWS(mCam,fb,(int)(x+Math.sin(angle)*r+0.5),(int)(y-Math.cos(angle)*r+0.5));
      v2.add(new SimpleVector(mCamX,mCamY,0));
      SimpleVector[] vectors = new SimpleVector[] { v1, v2 };
      fuelLine.update(vectors);

      fb.clear(back);
      world.renderScene(fb);
      world.draw(fb);

      fb.blit(fuelMeter,0,0,0,0,256,256,200,200,100,false);

      meterWorld.renderScene(fb);
      meterWorld.draw(fb);
      fb.display();
    }
  }
}

I have no idea which version of GL I'm using. I'm running this on an Android 2.3.5 and an Android 4.0.3 device.

Hope you can find anything. Tanx!

[attachment deleted by admin]

5
Support / Re: How to draw a simple 2D line onto my jPCT Framebuffer?
« on: September 09, 2013, 10:02:04 pm »
Almost there... only one tiny problem. I'm doing this in my onDrawFrame() method:

Code: [Select]
      fb.clear(back);
      world.renderScene(fb);
      world.draw(fb);

      fb.blit(fuelMeter,0,0,0,0,256,256,200,200,100,false);

      meterWorld.renderScene(fb);
      meterWorld.draw(fb);
      fb.display();

So first I draw my world containing the actual game objects, then I blit the static part of the fuel meter on top of it, and finally I draw the world containing nothing but my fuel indicator line on top of that. It all works great, except for 1 tiny detail:

The color of the polyline in the "meterWorld" is always white!? Even though it is defined as red:

Code: [Select]
fuelLine = new Polyline(new SimpleVector[] { new SimpleVector(-20,0,20), new SimpleVector(20,20,20) }, RGBColor.RED);
After some trail-and-error I found that removing the fb.blit() call fixes the issue, thus my Polyline is actually red then.

Why does this blit affect the color of the polyline? And how can I prevent that from happening?

6
Support / Re: How to draw a simple 2D line onto my jPCT Framebuffer?
« on: September 09, 2013, 03:40:39 pm »
That's it! I missed that line in the docs, see it now.
So simple. Tanx, working perfect now!

7
Support / Re: How to draw a simple 2D line onto my jPCT Framebuffer?
« on: September 09, 2013, 12:17:13 pm »
Hi Egon,

Using reproject2D3DWS() does not seem to change a lot, my line is still just 3 or 4 pixels in the middle of the screen. Also, I've noticed that when I move the cam around, the reprojected lines move as well. Even though I am calling reproject2D3DWS() after every camera move. The idea is that the lines remain static on screen, like an overlay.

As the camera is passed as an argument into the reproject2D3DWS() method, I expected the method to come up with 3D coordinates that result in the exact same 2D coordinates, even when the cam is moved.

I'm still missing a vital piece. I think we need an example here to clarify things. I used your "hello world" app to demonstrate what I'm doing:

Code: [Select]
package com.example.line2don3d;

import java.lang.reflect.Field;

import javax.microedition.khronos.egl.EGL10;
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.egl.EGLDisplay;
import javax.microedition.khronos.opengles.GL10;

import android.app.Activity;
import android.opengl.GLSurfaceView;
import android.os.Bundle;
import android.view.MotionEvent;

import com.threed.jpct.*;

public class HelloWorld extends Activity {

  public final static float PI = (float)Math.PI;
  public final static SimpleVector XAXIS = new SimpleVector(1,0,0);
  public final static SimpleVector YAXIS = new SimpleVector(0,1,0);
  public final static SimpleVector ZAXIS = new SimpleVector(0,0,1);
  public final static float DEGtoRAD = PI/180;

  // Used to handle pause and resume...
  private static HelloWorld master = null;

  private GLSurfaceView mGLView;
  private MyRenderer renderer = null;
  private FrameBuffer fb = null;
  private World meterWorld = null;
  private RGBColor back = new RGBColor(0,0,0);

  private float touchTurn = 0;
  private float touchTurnUp = 0;

  private float xpos = -1;
  private float ypos = -1;

  private Camera cam = null;
  private float camX = 100;
  private float camY = 0;

  private Polyline fuelLine = null;

  protected void onCreate(Bundle savedInstanceState) {

    Logger.log("onCreate");

    if (master != null) {
      copy(master);
    }

    super.onCreate(savedInstanceState);
    mGLView = new GLSurfaceView(getApplication());

    mGLView.setEGLConfigChooser(new GLSurfaceView.EGLConfigChooser() {
      public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display) {
        // Ensure that we get a 16bit framebuffer. Otherwise, we'll fall
        // back to Pixelflinger on some device (read: Samsung I7500)
        int[] attributes = new int[] { EGL10.EGL_DEPTH_SIZE, 16, EGL10.EGL_NONE };
        EGLConfig[] configs = new EGLConfig[1];
        int[] result = new int[1];
        egl.eglChooseConfig(display, attributes, configs, 1, result);
        return configs[0];
      }
    });

    renderer = new MyRenderer();
    mGLView.setRenderer(renderer);
    setContentView(mGLView);
  }

  @Override
  protected void onPause() {
    super.onPause();
    mGLView.onPause();
  }

  @Override
  protected void onResume() {
    super.onResume();
    mGLView.onResume();
  }

  @Override
  protected void onStop() {
    super.onStop();
  }

  private void copy(Object src) {
    try {
      Logger.log("Copying data from master Activity!");
      Field[] fs = src.getClass().getDeclaredFields();
      for (Field f : fs) {
        f.setAccessible(true);
        f.set(this, f.get(src));
      }
    } catch (Exception e) {
      throw new RuntimeException(e);
    }
  }

  public boolean onTouchEvent(MotionEvent me) {

    if (me.getAction() == MotionEvent.ACTION_DOWN) {
      xpos = me.getX();
      ypos = me.getY();
      return true;
    }

    if (me.getAction() == MotionEvent.ACTION_UP) {
      xpos = -1;
      ypos = -1;
      touchTurn = 0;
      touchTurnUp = 0;
      return true;
    }

    if (me.getAction() == MotionEvent.ACTION_MOVE) {
      float xd = me.getX() - xpos;
      float yd = me.getY() - ypos;

      xpos = me.getX();
      ypos = me.getY();

      touchTurn = xd / -100f;
      touchTurnUp = yd / -100f;
      return true;
    }

    try {
      Thread.sleep(15);
    } catch (Exception e) {
      // No need for this...
    }

    return super.onTouchEvent(me);
  }

  protected boolean isFullscreenOpaque() {
    return true;
  }

  class MyRenderer implements GLSurfaceView.Renderer {

    public MyRenderer() {
    }

    public void onSurfaceChanged(GL10 gl, int w, int h) {
      if (fb != null) {
        fb.dispose();
      }
      fb = new FrameBuffer(gl, w, h);

      if (master == null) {

        meterWorld = new World();
        meterWorld.setAmbientLight(100, 100, 100);

        Light sun = new Light(meterWorld);
        sun.setIntensity(255,255,255);
        sun.setPosition(new SimpleVector(100,0,0));

        cam = meterWorld.getCamera();
        cam.setPosition(camX,camY,0);
        cam.setOrientation(new SimpleVector(-1,0,0),ZAXIS);

        Object3D cube = Primitives.getCube(10);
        cube.setAdditionalColor(0xff,0xff,0);
        cube.setOrigin(SimpleVector.ORIGIN);
        cube.build();
        meterWorld.addObject(cube);

        fuelLine = new Polyline(new SimpleVector[] { new SimpleVector(-20,0,20), new SimpleVector(20,20,20) }, RGBColor.RED);
        fuelLine.setWidth(3);
        meterWorld.addPolyline(fuelLine);

        if (master == null) {
          Logger.log("Saving master Activity!");
          master = HelloWorld.this;
        }
      }
    }

    public void onSurfaceCreated(GL10 gl, EGLConfig config) {
    }

    private float fuel = 0;

    public void onDrawFrame(GL10 gl) {
      if (touchTurn != 0)
      {
        camY += touchTurn*10;
        cam.setPosition(camX,camY,0);
        cam.setOrientation(new SimpleVector(-1,0,0),ZAXIS);
        touchTurn = 0;
      }

      if (touchTurnUp != 0)
      {
        camX += touchTurnUp*10;
        cam.setPosition(camX,camY,0);
        cam.setOrientation(new SimpleVector(-1,0,0),ZAXIS);
        touchTurnUp = 0;
      }

      fuel += 0.01; // make the fuel line spin around and  around

      float angle = (-60+fuel*120)*DEGtoRAD;
      int x = 200;
      int y = 200;
      float r = 50;

      SimpleVector[] vectors = new SimpleVector[]
        {
          Interact2D.reproject2D3DWS(cam,fb,x,y),
          Interact2D.reproject2D3DWS(cam,fb,(int)(x+Math.sin(angle)*r+0.5),(int)(y-Math.cos(angle)*r+0.5))
        };
      fuelLine.update(vectors);

      fb.clear(back);
      meterWorld.renderScene(fb);
      meterWorld.draw(fb);
      fb.display();
    }
  }
}

What I'm getting with this, is a yellow cube on the origin, and inside the cube (so zoom in A LOT by sliding down) I find my little line that is spinning around. What I want is a spinning line around the actual sceen coordinate 200,200 with a radius of 50 pixels. What to do?

8
Support / Re: How to draw a simple 2D line onto my jPCT Framebuffer?
« on: September 06, 2013, 02:42:02 am »
Hi Egon,

So I went for the Polyline/Interact2D solution... spend hours and hours, but I can't get it right. I must be missing something...
Here's basically what I'm doing:

init function:

Code: [Select]
Polyline fuelLine = new Polyline(new SimpleVector[] { new SimpleVector(-1,0,1), new SimpleVector(1,1,1) }, RGBColor.RED);
world.addPolyline(fuelLine);

Then in onDrawFrame():

Code: [Select]
// determine center, radius and the line-angle of the fuel meter
int x = 100;
int y = 100;
float r = 40;
float angle = ...;

world.renderScene(framebuffer);
cam = world.getCamera();
SimpleVector[] vectors = new SimpleVector[]
{
  Interact2D.reproject2D3D(cam,framebuffer,x,y),
  Interact2D.reproject2D3D(cam,framebuffer,(int)(x+Math.sin(angle)*r+0.5),(int)(y-Math.cos(angle)*r+0.5))
};
fuelLine.update(vectors);

world.draw(framebuffer);

It does produce a line, but it's nowhere near the 2D x,y coordinates that I'm using, but somewhere in the middle of the screen (size 480x800px). Also the resulting line is much too short. I expect a line of 40 pixels, but it's actually like 2 or 3 pixels.

What am I doing wrong?

9
Support / Re: How to draw a simple 2D line onto my jPCT Framebuffer?
« on: September 04, 2013, 05:01:25 pm »
Yes, like a real fuel indicator you find in most cars. However, I do not want to draw this line in 3D space. It must somehow be possible to blit a line directly into jPCT's Framebuffer, so in real plain old 2D.

I've come up with the following:

Code: [Select]
Bitmap bmp = Bitmap.createBitmap(100,100,Bitmap.Config.RGB_565);
Canvas c = new Canvas(bmp);
Paint p = new Paint();
p.setColor(0xffffffff);

then in the onDrawFrame() method:

Code: [Select]
framebuffer.blit(fuelMeter,0,0,10,10,100,100,true); // blit the fuel meter without the line-indicator
// then make the line-indicator
bmp.eraseColor(0);
c.drawLine(10,10,90,90,p); // this is a static line, but of course the coordinates should move
Texture t = new Texture(bmp);
framebuffer.blit(t,0,0,10,10,100,100,true);

It works OK, except after a few seconds I get an Out of Memory error:

Code: [Select]
09-04 16:40:53.240: E/dalvikvm-heap(18607): Out of memory on a 65552-byte allocation.
09-04 16:40:53.240: I/dalvikvm(18607): "GLThread 6483" prio=5 tid=13 RUNNABLE
09-04 16:40:53.240: I/dalvikvm(18607):   | group="main" sCount=0 dsCount=0 obj=0x41244200 self=0x2170230
09-04 16:40:53.240: I/dalvikvm(18607):   | sysTid=18628 nice=0 sched=0/0 cgrp=default handle=34429144
09-04 16:40:53.240: I/dalvikvm(18607):   | schedstat=( 0 0 0 ) utm=874 stm=256 core=0
09-04 16:40:53.240: I/dalvikvm(18607):   at com.threed.jpct.GLRenderer.convertTexture(GLRenderer.java:~868)
09-04 16:40:53.240: I/dalvikvm(18607):   at com.threed.jpct.GLRenderer.blit(GLRenderer.java:1381)
09-04 16:40:53.240: I/dalvikvm(18607):   at com.threed.jpct.GLRenderer.blitTexture(GLRenderer.java:1747)

So probably instantiating a new Texture on every frame is not a good idea, but I see no possibility to re-use an existing Texture. Or maybe I'm using an incorrect approach alltogether.

Any help would be appreciated!

10
Support / How to draw a simple 2D line onto my jPCT Framebuffer?
« on: September 04, 2013, 02:53:21 pm »
Hi,

I'm developing a little 3D game in which you drive a car through a 3D world. In the upper left corner I'm displaying a fuel indicator, which is of course in simple flat 2D.

I'm using framebuffer.blit() to put the indicator itself directly on top of every frame, which is working fine.
However, inside the fuel indicator I need to draw a line, that actually indicates the level of fuel.

In the desktop version of jPCT, FrameBuffer has a getGraphics() method. But, since on Android there is no AWT, this method does not exist there. Now how do I draw my simple line?

Tnx!

11
Support / Re: Rotate a group of objects from one vector to another
« on: April 25, 2013, 12:04:17 am »
Hi Egon,

It's a good idea to come up with the needed angles right away from the calculations I'm doing anyway, still need to look at that. Meanwhile I took a look at the SimpleVector.getRotationMatrix() method you suggested... wondering what I could do with it, it turned out, that this was the solution for my question 8)

I thought I'd post the solution here, just in case someone stumbles upon this page with a similar question.

Code: [Select]
Object3D[] group = something1;
SimpleVector original = something2;
SimpleVector destination = something3;

Matrix matrix = original.getRotationMatrix().invert();
matrix.matMul(destination.getRotationMatrix());

for (Object3D o: group)
{
  o.setRotationMatrix(matrix);
}

Tnx 4 your help!

12
Support / Re: Rotate a group of objects from one vector to another
« on: April 23, 2013, 09:12:19 pm »
Hi Egon,

I'm not sure that I understand what you mean by that, but I'd say that is not what I want.
Let me try to explain my problem a bit more practically:

Imagine a persons' lower arm, from the elbow to the wrist. The initial position of the arm is represented by vector 1. Now my "hero" moves, and I've calculated a new vector (vector 2) that represents the new position the arm should be in.

So now, somehow it must be possible to use the difference in angles between those two vectors and use those angles to rotate the objects that actually make up the lower arm. All those objects have the same pivot point, which is of course the elbow.

I hope this clarifies what I'm trying to do :)

Tnx!

13
Support / Rotate a group of objects from one vector to another
« on: April 23, 2013, 03:38:41 pm »
Hi,

I have a group of Object3D's which I want to rotate "as a whole" into a different direction.

First, I'm giving all the objects the same rotation pivot point.
Then, I come up with a single SimpleVector which represents the current direction of the object group.
Then, after some calculations, I come up with a new SimpleVector, which indicates the desired new direction of my objects.

My question now is, how to use these 2 vectors to rotate my objects?

So let's say:

Code: [Select]
Object3D[] group = something1;
SimpleVector original = something2;
SimpleVector destination = something3;

for (Object3D o: group)
{
   // what to do with o?
}

Tnx!

14
Support / Re: Way to rotate loaded model permanently
« on: April 08, 2013, 08:28:02 pm »
You do react fast, fantastic!

I was afraid of that, but simply rotating the encapsulating dummyObject seems a lot easier. All sub-objects then rotate in one go around the same axis, which works nicely. So the sub-objects do get rotated.

I've also tried calling rotateMesh() on each of the sub-parts, but that has no effect.

I'm sure there is a good explanation, but I simply don't see it. In my head, all the objects are correctly presented on the view, and I simply want that view to be the default.

I will try rotating all the sub-objects one by one around the same axis, and I'm sure that will work. Wouldn't it make sense if my approach would also work?

Thanks

15
Support / Re: Way to rotate loaded model permanently
« on: April 08, 2013, 04:56:16 pm »
I'm doing (almost) the same thing, but somehow rotateMesh() does not make the rotation permanent in my case.

I'm loading a 3DS model using Loader.load3DS(). Like bLAZY I also need to permanently rotate the object. My object consists of more than 1 object, so I'm wrapping them in a dummyObject first, which then is rotated:

Code: [Select]
Object3D[] puppetParts  = Loader.load3DS(stream,.1f);
Object3D puppet = Object3D.createDummyObj();
for (Object3D part: puppetParts)
  puppet.addChild(part);
puppet.rotateZ(-PI/2); // make puppet face positive X axis
puppet.rotateMesh(); // should make rotation permanent
puppet.clearRotation(); // but this rotates my puppet back!?
world.addObjects(puppetParts);

What am I doing wrong?

Thanks!

Pages: [1]