I am currently working on an android app, and I want to include ARToolKit to scan pictures and show 3d models.
So I added the ARToolKit 6 to my Android Studio and I created the program itself fast. Next I wanted to add markers with addMarker, so basically my code was this.
class ARTrackingRenderer extends ARRenderer {
private static final class Trackable{
String name;
float height;
Trackable(String name, float height)
{
this.name = name;
this.height = height;
}
}
private static final Trackable trackables[] = new Trackable[]{
new Trackable("Alterra_Ticket_1.jpg", 95.3f),
new Trackable("Alterra_Postcard_2.jpg", 95.3f),
new Trackable("Alterra_Postcard_3.jpg", 127.0f),
new Trackable("Alterra_Postcard_4.jpg", 95.3f)
};
private int trackableUIDs[] = new int[trackables.length];
private Cube cube;
private Cube cube2;
#Override
public boolean configureARScene() {
int i = 0;
for (Trackable trackable : trackables){
trackableUIDs[i] = ARToolKit.getInstance().addMarker("2d;Data/2d/" + trackable.name + ";" + trackable.height);
if (trackableUIDs[1] < 0) return false;
}
NativeInterface.arwSetTrackerOptionInt(NativeInterface.ARW_TRACKER_OPTION_2D_MAX_IMAGES, trackables.length);
return true;
}
//Shader calls should be within a GL thread. GL threads are onSurfaceChanged(), onSurfaceCreated() or onDravFrame()
//As the cube instantiates the shader during setShaderProgram call we need to create the cube here.
#Override
public void onSurfaceCreated(GL10 unused, javax.microedition.khronos.egl.EGLConfig config){
this.shaderProgram = new SimpleShaderProgram(new SimpleVertexShader(), new SimpleFragmentShader());
cube = new Cube(30.0f, 0.0f, 0.0f, 0.0f); //Das erste ist die Größe, das zweite die horizontale Ausrichtung, das dritte die vertikale und das vierte der Abstand vom boden aus
cube2 = new Cube(15.0f, 50.0f, 0.0f, 20.0f);
cube.setShaderProgram(shaderProgram);
cube2.setShaderProgram(shaderProgram);
super.onSurfaceCreated(unused, config);
}
#Override
public void draw(){
super.draw();
GLES20.glEnable(GLES20.GL_CULL_FACE);
GLES20.glEnable(GLES20.GL_DEPTH_TEST);
GLES20.glFrontFace(GLES20.GL_CCW);
for (int trackableUID:trackableUIDs){
if (ARToolKit.getInstance().queryMarkerVisible(trackableUID)){
float[] projectionMatrix = ARToolKit.getInstance().getProjectionMatrix();
float[] modelVievMatrix = ARToolKit.getInstance().queryMarkerTransformation(trackableUID);
cube.draw(projectionMatrix, modelVievMatrix);
cube2.draw(projectionMatrix, modelVievMatrix);
}
}
}
}
I got the code from a Tutorial on YouTube. I want to add new markers. How can I do this? Or basically just how can I add new pictures, so it wont scan everytime the alterra pictures?
Thanks for all help.
This code:
private static final Trackable trackables[] = new Trackable[]{
new Trackable("Alterra_Ticket_1.jpg", 95.3f),
new Trackable("Alterra_Postcard_2.jpg", 95.3f),
new Trackable("Alterra_Postcard_3.jpg", 127.0f),
new Trackable("Alterra_Postcard_4.jpg", 95.3f)
};
Is responsible for adding the 'markers' called Trackable to ARToolKit. You can add your markers to the trackables array.
The .jpg files need to be available in the assets directory parallel to the Alterra*.jpg files.
Only .jpg files are possible.
Related
I've created an Android application to show about a hundred point objects using Libgdx modelBuilder. It works fine and models are rendered without any problem, but I needed to add a marker for every point model. To do that I've extended from Actor and in this class, I've created two textures representing the marker in normal and the selected mode when the user clicked the objects. The following is the LabelActor class which has been instantiated for each point model.
public class LabelActor extends Actor {
Texture texture;
Texture selectedTexture;
Sprite sprite;
private Vector3 distVector = new Vector3();
public LabelActor( String markerColor){
String path = "markers/point_marker_" + markerColor + ".png"";
String selected_path = "markers/point_marker_select.png";
texture = new Texture(Gdx.files.internal(path));
selectedTexture = new Texture(Gdx.files.internal(selected_path));
sprite = new Sprite(texture);
setBounds(sprite.getX(), sprite.getY(), sprite.getWidth(), sprite.getHeight());
}
#Override
protected void positionChanged() {
sprite.setPosition(getX(),getY());
super.positionChanged();
}
#Override
public void draw(Batch batch, float parentAlpha) {
if (selected){
batch.draw(selectedTexture, getX()-sprite.getWidth()/2, getY());
} else {
batch.draw(texture, getX()-sprite.getWidth()/2, getY());
}
}
}
The problem will be arisen on using the marker for these objects which causes a huge amount of memory usage. Loading a sphere model for each point objects takes about 14M of graphics memory but loading texture markers take 500M of memory on the device. I've used png icons located in the asset folders to create my textures but not using a Libgdx atlas. Is there any chance to create markers for this number of point objects to consume a little amount of memory?
Below is the image representing the view of the markers and point models.
Edit 1:
I've used AssetManager to load textures and create HashMap of them in the main screen then pass an array of two textures to each LabelActor class as Retron advised, but the memory is still overloaded.
public class LabelActor extends Actor {
Texture texture;
Texture selectedTexture;
Sprite sprite;
private Vector3 distVector = new Vector3();
public LabelActor(Texture[] textures){
texture = textures[0];
selectedTexture = textures[1];
sprite = new Sprite(texture);
setBounds(sprite.getX(), sprite.getY(), sprite.getWidth(), sprite.getHeight());
}
#Override
protected void positionChanged() {
sprite.setPosition(getX(),getY());
super.positionChanged();
}
#Override
public void draw(Batch batch, float parentAlpha) {
if (selected){
batch.draw(selectedTexture, getX()-sprite.getWidth()/2, getY());
infoWindow.draw(batch, parentAlpha);
} else {
batch.draw(texture, getX()-sprite.getWidth()/2, getY());
}
}
}
Part of main screen to load textures using AssetManager:
#Override
public void create() {
...
environment = new Environment();
environment.set(new ColorAttribute(ColorAttribute.AmbientLight, 0.4f, 0.4f, 0.4f, 1.0f));
pointLight = new PointLight().set(0.8f, 0.8f, 0.8f, 2f, 0f, 0f, 500f);
environment.add(pointLight);
assetManager = new AssetManager();
textureTitleList = new ArrayList<>();
for (FileHandle f: Gdx.files.internal("markers").list()){
assetManager.load("markers/" + f.name(), Texture.class);
textureTitleList.add(f.name());
}
}
private void loadModel() {
textureMap = new HashMap<>();
for (String fName: textureTitleList){
textureMap.put(fName, assetManager.get("markers/" + fName, Texture.class));
}
}
#Override
public void render() {
if (!isLoaded && assetManager.update()) {
loadModel();
isLoaded = true;
}
...
}
You must use AssetManager and load ONE texture from asset, not create new texture for every new actor.
I made a 3D environment with a few 3D objects using rajawali. I set up a directionnal light, everything displays, I added diffuse material to every object.
But I can not have the shadow of one object on another.
The objects have their own shadows on themselves depending on how they are oriented within the directionnal light but they doesnt seem to have shadows of other objects. I also tried with a spotlight but it didnt work.
What can I do ? Is there a feature to enable or something ?
Here is my code :
public class BasicRenderer extends Renderer {
private Sphere mEarthSphere;
private DirectionalLight mDirectionalLight;
private SpotLight mSpotLight;
private Object3D[][][] mCubes;
private Object3D mRootCube;
public BasicRenderer(Context context) {
super(context);
setFrameRate(60);
}
#Override
protected void initScene() {
getCurrentScene().setBackgroundColor(0, 0.5f, 1.0f, 1.0f);
getCurrentScene().setFog(new FogMaterialPlugin.FogParams(FogMaterialPlugin.FogType.LINEAR, 0x999999, 50, 100));
mSpotLight = new SpotLight();
mSpotLight.setPower(20);
mSpotLight.enableLookAt();
mSpotLight.setPosition(new Vector3(2, 1, 0));
mSpotLight.setLookAt(0,0,0);
//getCurrentScene().addLight(mSpotLight);
mDirectionalLight = new DirectionalLight(-1f, -2f, -1.0f);
mDirectionalLight.setColor(1.0f, 1.0f, 1.0f);
mDirectionalLight.setPower(1.5f);
getCurrentScene().addLight(mDirectionalLight);
SpecularMethod.Phong phongMethod = new SpecularMethod.Phong(0xeeeeee, 200);
Material material = new Material();
material.enableLighting(true);
material.setDiffuseMethod(new DiffuseMethod.Lambert());
//material.setSpecularMethod(phongMethod);
Texture earthTexture = new Texture("Earth", R.drawable.earthtruecolor_nasa_big);
NormalMapTexture earthNormal = new NormalMapTexture("earthNormal", R.drawable.earthtruecolor_nasa_big_n);
earthTexture.setInfluence(.5f);
try{
material.addTexture(earthTexture);
material.addTexture(earthNormal);
} catch (ATexture.TextureException error){
Log.d("BasicRenderer" + ".initScene", error.toString());
}
material.setColorInfluence(0);
mEarthSphere = new Sphere(1, 24, 24);
mEarthSphere.setMaterial(material);
getCurrentScene().addChild(mEarthSphere);
getCurrentCamera().setZ(4.2f);
mCubes = new Object3D[30][30][2];
Material cubeMaterial = new Material();
cubeMaterial.enableLighting(true);
cubeMaterial.setDiffuseMethod(new DiffuseMethod.Lambert(1));
//cubeMaterial.setSpecularMethod(phongMethod);
cubeMaterial.enableTime(true);
cubeMaterial.setColorInfluence(0);
Texture cubeTexture = new Texture("Stone", R.drawable.stone);
try{
cubeMaterial.addTexture(cubeTexture);
} catch (ATexture.TextureException error){
Log.d("BasicRenderer" + ".initScene", error.toString());
}
cubeMaterial.addPlugin(new DepthMaterialPlugin());
mRootCube = new Cube(1);
mRootCube.setMaterial(cubeMaterial);
mRootCube.setY(-1f);
// -- similar objects with the same material, optimize
mRootCube.setRenderChildrenAsBatch(true);
getCurrentScene().addChild(mRootCube);
mCubes[0][0][0] = mRootCube;
for(int z = 0; z < 2; z++) {
for(int y = 0; y < 5; y++) {
for (int x = 0; x < 30; x++) {
Object3D cube = mRootCube.clone(true);
cube.setY(-5 + y);
cube.setX(-15 + x);
cube.setZ(z);
mRootCube.addChild(cube);
mCubes[x][y][z] = cube;
}
}
}
Object3D cube = mRootCube.clone(true);
cube.setY(0);
cube.setX(-15 + 10);
cube.setZ(1);
mRootCube.addChild(cube);
mCubes[5][10][1] = cube;
getCurrentScene().addChild(mCubes[5][10][1]);
// -- create a chase camera
// the first parameter is the camera offset
// the second parameter is the interpolation factor
ChaseCamera chaseCamera = new ChaseCamera(new Vector3(0, 3, 16), mEarthSphere);
// -- tell the camera which object to chase
// -- set the far plane to 1000 so that we actually see the sky sphere
chaseCamera.setFarPlane(1000);
getCurrentScene().replaceAndSwitchCamera(chaseCamera, 0); //<--Also the only change!!!
}
#Override
public void onRender(final long elapsedTime, final double deltaTime) {
super.onRender(elapsedTime, deltaTime);
mEarthSphere.rotate(Vector3.Axis.Y, 1.0);
}
}
As you can see on the next picture, there is no shadow on the ground next to the cube on the left, but I think there should be.
https://i.imgur.com/qtl6mZf.jpg
Most of the Libgdx tutorials I found show how to add 2D elements in a 3D world, but I would like to know how to the the opposite, adding 3D elements in a 2D Stage.
I tried adding a background image to the Stage, then adding to the Stage an Actor that renders the model batch and the 3D instances in its draw() method.
But instead, the image isn't drawn and part of the 3D object is hidden.
SimpleGame class
public class SimpleGame extends ApplicationAdapter {
Stage stage;
#Override
public void create () {
stage = new Stage();
InputMultiplexer im = new InputMultiplexer(stage);
Gdx.input.setInputProcessor( im );
Image background = new Image(new Texture("badlogic.jpg"));
background.setSize(stage.getWidth(), stage.getHeight());
stage.addActor(background);
setup();
}
private void setup() {
SimpleActor3D group = new SimpleActor3D();
group.setSize(stage.getWidth(), stage.getHeight());
group.setPosition(0, 0);
stage.addActor(group);
}
#Override
public void render () {
stage.act();
Gdx.gl.glClearColor(1, 1, 1, 1);
Gdx.gl.glViewport(0, 0, Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
Gdx.gl.glClear( GL20.GL_COLOR_BUFFER_BIT | GL20.GL_DEPTH_BUFFER_BIT );
stage.draw();
}
}
SimpleActor3D class
public class SimpleActor3D extends Actor {
public Environment environment;
public PerspectiveCamera camera;
public ModelBatch modelBatch;
public ModelInstance boxInstance;
public SimpleActor3D() {
environment = SimpleUtils.createEnvironment();
camera = SimpleUtils.createCamera();
boxInstance = SimpleUtils.createModelInstance(Color.GREEN);
modelBatch = new ModelBatch();
}
#Override
public void draw(Batch batch, float parentAlpha) {
Gdx.gl.glViewport((int)getX(), (int)getY(), (int)getWidth(), (int)getHeight());
modelBatch.begin(camera);
modelBatch.render( boxInstance, environment );
modelBatch.end();
super.draw(batch, parentAlpha);
}
}
SimpleUtils class
public class SimpleUtils {
public static Environment createEnvironment() {
Environment environment = new Environment();
environment.set( new ColorAttribute(ColorAttribute.AmbientLight, 0.4f, 0.4f, 0.4f, 1f) );
DirectionalLight dLight = new DirectionalLight();
Color lightColor = new Color(0.75f, 0.75f, 0.75f, 1);
Vector3 lightVector = new Vector3(-1.0f, -0.75f, -0.25f);
dLight.set( lightColor, lightVector );
environment.add( dLight ) ;
return environment;
}
public static PerspectiveCamera createCamera() {
PerspectiveCamera camera = new PerspectiveCamera(67, Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
camera.position.set(10f, 10f, 10f);
camera.lookAt(0,0,0);
camera.near = 1f;
camera.far = 300f;
camera.update();
return camera;
}
public static ModelInstance createModelInstance(Color color) {
ModelBuilder modelBuilder = new ModelBuilder();
Material boxMaterial = new Material();
boxMaterial.set( ColorAttribute.createDiffuse(color) );
int usageCode = VertexAttributes.Usage.Position + VertexAttributes.Usage.ColorPacked + VertexAttributes.Usage.Normal;
Model boxModel = modelBuilder.createBox( 5f, 5f, 5f, boxMaterial, usageCode );
return new ModelInstance(boxModel);
}
}
What I would like :
What I have instead :
I have tried rendering the model batch directly in the ApplicationAdapter render() method and it works perfectly, so the problems must lie somewhere with the Stage but I can't find how.
I had the same problem but I needed to render 3d object only once so I came with an idea to render 3d model as a Sprite. In order to do that I rendered my model via modelBatch to frame buffer object instead of default screen buffer and then created a sprite from FBO color buffer.
Sample code below:
FrameBuffer frameBuffer = new FrameBuffer(Pixmap.Format.RGBA8888, Gdx.graphics.getBackBufferWidth(), Gdx.graphics.getBackBufferHeight(), true);
Sprite renderModel(ModelInstance modelInstance) {
frameBuffer.begin(); //Capture rendering to frame buffer.
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT | GL20.GL_DEPTH_BUFFER_BIT | (Gdx.graphics.getBufferFormat().coverageSampling ? GL20.GL_COVERAGE_BUFFER_BIT_NV : 0))
modelBatch.begin(camera);
modelBatch.render(modelInstance);
modelBatch.end();
frameBuffer.end();
return new Sprite(frameBuffer.getColorBufferTexture());
}
You can always update your sprite texture in a render loop with use of sprite.setTexture(); method. You can also create an Image from a texture -> new Image(frameBuffer.getColorBufferTexture()); and use it in Scene2d.
Hi i'm making some App with QCAR & JPCT-AE. can SOMEONE see my source code & advice me? if you can, i'll send my source code to you. my email address is lyhdra99#gmail.com.
Please help me ^^
first
i send modelViewMatrix(QCAR::Matrix44F) from JNI to JAVA
ex) JNIEXPORT jfloatArray JNICALL Java_jp_may_com_VirtualButtonsRenderer_getNowMatrix(JNIEnv* env, jobject obj)
use this modelViewMatrix like below
public class VirtualButtonsRenderer implements GLSurfaceView.Renderer {
public VirtualButtonsRenderer(Activity act) {
Config.maxAnimationSubSequences = 999;
// TODO Auto-generated constructor stub
this.act = act;
_Object3D = Loader.loadMD2(act.getResources().openRawResource(R.raw.tris), 1.0f);
_Object3D.setName("MyTarget");
}
public native float[] getNowMatrix();
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
initRendering();
QCAR.onSurfaceCreated();
world = new World();
world.setAmbientLight(20, 20, 20);
TextureManager tm = TextureManager.getInstance();
com.threed.jpct.Texture Cover = new com.threed.jpct.Texture(BitmapFactory.decodeStream(act.getResources().openRawResource(R.raw.skin)));
tm.addTexture("Cover", Cover);
_Object3D.setTexture("Cover");
world.addObject(_Object3D);
world.buildAllObjects();
sun = new Light(world);
sun.setIntensity(250, 250, 250);
Camera cam = world.getCamera();
cam.moveCamera(Camera.CAMERA_MOVEOUT, 100);
cam.lookAt(_Object3D.getTransformedCenter());
SimpleVector sv = new SimpleVector();
sv.set(_Object3D.getTransformedCenter());
sv.x -= 300;
sv.z -= 0;
sun.setPosition(sv);
MemoryHelper.compact();
}
public void onDrawFrame(GL10 gl) {
if (!mIsActive)
return;
if (renderFrame()) {
Matrix NowMatrix = new Matrix();
NowMatrix.fillDump(getNowMatrix());
world.getCamera().setBack(NowMatrix);
world.renderScene(fb);
world.draw(fb);
fb.display();
return;
} else {
mIsTouch = false;
}
}
}
here, i got problem. i thought Object3D can move like Teapot(QCAR Sample Object) on Marker with modelViewMatrix. but it couldn't.
this is my problem ^^;;
i would like to help you with your app,
please send me your native imagetarget.cpp code,
before that i think you have seen this page ,refer this once
http://www.jpct.net/wiki/index.php/Integrating_JPCT-AE_with_Vuforia
its project source code
https://github.com/sidneibjunior/vuforia-jpct
fetch your modelViewMatrix from renderframe and send it to java , like this
const QCAR::TrackableResult* result = state.getTrackableResult(tIdx);
QCAR::Matrix44F modelViewMatrix = QCAR::Tool::convertPose2GLMatrix(result->getPose());
SampleUtils::rotatePoseMatrix(90.0f, 1.0f, 0, 0, &modelViewMatrix.data[0]);
//inversing the matrix
QCAR::Matrix44F inverseMV = SampleMath::Matrix44FInverse(modelViewMatrix);
//transposing the inverted matrix
QCAR::Matrix44F invTranspMV = SampleMath::Matrix44FTranspose(inverseMV);
send the inverse transpose matrix to java code. It will work fine... i hope :)
I'm creating an android app using Andengine. One part of the app requires users to select a few sprites from a group of sprites on the screen, which causes the selected sprites to turn a different color (ie, moving to the next tile). I declared them all as animated sprites and I'm using the same texture for each one. The problem is that once I select a sprite, every sprite moves to the next tile, not just the one I selected. How do I make just the one sprite change?
Here's where I setup the textures and whatnot:
private Texture mGreenTextureAtlas;
private TiledTextureRegion mGreenBallFaceTextureRegion;
#Override
public void onLoadResources() {
/* Textures. */
...
this.mGreenTextureAtlas = new Texture(32, 32, TextureOptions.BILINEAR_PREMULTIPLYALPHA);
...
TextureRegionFactory.setAssetBasePath("gfx/");
/* TextureRegions. */
...
this.mGreenBallFaceTextureRegion = TextureRegionFactory.createTiledFromAsset(this.mGreenTextureAtlas, this, "green_ball.png", 0, 16, 2, 1); // 64x32
this.mEngine.getTextureManager().loadTextures(this.mCueTextureAtlas, this.mGreenTextureAtlas , this.mBackgroundTexture, this.mPocketTexture);
}
Here's where I actually create the sprites and apply the textures:
face = new AnimatedSprite(pX, pY, this.mGreenBallFaceTextureRegion);
body = PhysicsFactory.createCircleBody(this.mPhysicsWorld, face, BodyType.DynamicBody, FIXTURE_DEF);
encapsed = new Encapsulator(body, face, Encapsulator.AVOID_BALL, mFaceCount);
ballsList.add(encapsed);
I encapsulate each sprite, it's body, and some other data into an object that I made, and then add that object into an ArrayList.
Here is the onTouch event handler.
#Override
public boolean onAreaTouched( final TouchEvent pSceneTouchEvent, final ITouchArea pTouchArea,final float pTouchAreaLocalX, final float pTouchAreaLocalY) {
if(pSceneTouchEvent.isActionDown()) {
final AnimatedSprite face = (AnimatedSprite) pTouchArea;
for(int i=0; i<ballsList.size(); i++)
{
if(face.equals(ballsList.get(i).animatedFace))
{
ballsList.get(i).toggleType(face);
System.out.println("Ball " + ballsList.get(i).id + " is now " + ballsList.get(i).type);
}
}
return true;
}
return false;
}
Finally, here is the toggleType method in the Encapsulator class that's responsible for moving to the next tile:
public void toggleType(AnimatedSprite face)
{
if(this.type == AVOID_BALL)
{
this.type = HIT_BALL;
face.nextTile();
}
else if(this.type == HIT_BALL)
{
this.type = AVOID_BALL;
face.setCurrentTileIndex(0);
}
}
Sorry if this is a bit long-winded. Any help is appreciated.
I did some more googling and came across a solution. I had to use the textureregion.clone() method when creating the sprites. I found the solution at this link:
http://www.andengine.org/forums/development/two-sprites-sharing-the-same-tiledtextureregion-t4339.html