I got to that point when I have to replace all new Sprite(texture) with new Sprite(manager.get(imgPath,Texture.class)) in order to optimize sprites creating and do all the loading before starting the game.
The problem is that when I use a common AssetManager to load all textures I get black rectangles.
Still, using a new AssetManager() each time I need to load a Texture works fine (maybe because this manager is quickly garbadge-collected and doesn't have time to dispose the texture it created?) but that way there is no point of using it.
The setupBody() method below is called at the end of my sprite-body wrapper constructor.
the working code (yet useless) ─ one new AssetManager for each sprite:
protected void setupBody(String path){
BodyEditorLoader loader = new BodyEditorLoader(Gdx.files.internal(path));
BodyDef bDef = new BodyDef();
bDef.type = BodyDef.BodyType.DynamicBody;
bDef.position.set(this.position);
bDef.angle = this.rotation;
body = mWorld.createBody(bDef);
body.setUserData(this);
FixtureDef fix = new FixtureDef();
fix.density = 0.1f;
fix.friction = 0f;
fix.restitution = 0.5f;
fix.filter.categoryBits = this.getCategory();
fix.filter.maskBits = this.getMask();
origin = loader.getOrigin("base"+identifier, 1).cpy();
String imgPath = loader.getImagePath("base"+identifier);
AssetManager manager = new AssetManager();
manager.load(imgPath,Texture.class);
manager.finishLoading();
Texture baseSpriteTexture = manager.get(imgPath,Texture.class);
Texture baseSpriteTexture = new Texture(imgPath);
baseSprite = new Sprite(baseSpriteTexture);
loadHighlightedSprite(loader, identifier);
attachFixture(loader, identifier, fix);
}
the code keeping me sleepless ─ the idea is to have a single non-static manager :
protected void setupBody(String path){
/*no changes before */
String imgPath = loader.getImagePath("base"+identifier);
AssetManager manager = SmallIntestineDemoGame.getAssets();
manager.load(imgPath,Texture.class);
/* no changes after */
}
SmallIntestineDemoGame$getAssets() :
public class SmallIntestineDemoGame extends Game {
public AssetManager assets;
#Override
public void create() {
setScreen(new GameScreen());
}
public static AssetManager getAssets() {
return ((SmallIntestineDemoGame) Gdx.app.getApplicationListener()).assets;
}
public void setupAssets(){
this.assets = new AssetManager();
Texture.setAssetManager(assets);
}
}
DEBUGGING :
Debugging my asset manager got me confused. size of values of values of assets map of manager gets stuck at 33 even if I'm adding more:
(source: hostingpics.net)
I tested both cases ─ the app with a new AssetManager() (followed by finishloading and all required stuff) for each sprite creation. AND with a unique asset manager passed to all sprite creations :
I set a breakpoint at draw method and there was almost no difference between the two sprites, except for sprite.texture.glhandle !
When all sprites textures are created using the same manager their textures glhandle is set to 0 and this explains the black rectangles. But when each sprite is created using a locally newly created asset manager, the glhandle has some proper value (2269).
Texture.setAssetManager(assets);
When you set AssetManager then you need to call update() method of AssetManager each time when your game resumed.
If you don't set the AssetManager, the usual managed texture mechanism will kick in, so you don't have to worry about anything.
Related
I am learning how to use libgdx and i dont found an awsnser to this either for here or docs.
My problem is very clear in the title. When i try to get a texture loaded 5 seconds before ( this is because i am using a Gdx timer, only for testing) the aplication crashes and gives this
Exception in thread "LWJGL Application" java.lang.ClassCastException: com.badlogic.gdx.graphics.Texture cannot be cast to com.badlogic.gdx.scenes.scene2d.ui.Image
at com.hoticecream.mad.Background.setStaticBackground(Background.java:33)
at com.hoticecream.mad.Playgorund$1.run(Playgorund.java:102)
at com.badlogic.gdx.backends.lwjgl.LwjglApplication.executeRunnables(LwjglApplication.java:257)
at com.badlogic.gdx.backends.lwjgl.LwjglApplication.mainLoop(LwjglApplication.java:208)
at com.badlogic.gdx.backends.lwjgl.LwjglApplication$1.run(LwjglApplication.java:124)
i will post the whole class because is short
class Background {
private Image background;
private final String backgroundName;
private AssetManager manager;
Background(AssetManager manager){
this.manager = manager;
background = new Image();
backgroundName = "background1.png";
manager.load(backgroundName, Texture.class);
manager.finishLoadingAsset(backgroundName);
}
public void setStaticBackground(Stage stage){
//here is where error happends, affter manager.update() returning true
if(manager.update()){
background = manager.get(backgroundName);
}else {
Gdx.app.log("Manager","not loaded");
}
background.setHeight(stage.getHeight());
background.setWidth(stage.getWidth());
stage.addActor(background);
}
public void dispose(){
manager.unload(backgroundName);
}
}
slide note: the asset manager is passed by argument from inner class
Apparently, the object (it's texture, because of the set loader type - manager.load(backgroundName, Texture.class);) you are getting from the manager is not castable to Image type.
Try to create an Image object from the texture:
...
if(manager.update()){
background = new Image((Texture) manager.get(backgroundName));
...
I know most questions on the community should be done with at least some code. But I´m totally lost here, I don´t even know where to start. What I want to do is use the Vuforia AR Library to render LibGDX 3D modelInstances. However, I don't know how can I make Vuforia render the modelInstances or use a libGDX camera as it´s camera.
I've done external research but I have not been able to find useful information. Is there anyone who can help me get started with this?
Please note that I am not versed in Vuforia AR specifically, but the question has gone unanswered for a while, so I will give it a shot.
The Camera in LibGDX is essentially just a wrapper for two 4x4 matrices, the view matrix Camera#view, and the projection matrix Camer#projection (there is another Matrix, the model matrix, used for world space transformations, however I believe [but am not 100% certain] that in LibGDX, this matrix is already incorporated into the view matrix [so Camera#view is actually the model-view matrix]).
Anyway, following on from this, unless there is a simpler solution that I am not aware of, you should be able to use these underlying matrices to deal with projections between the the Vuforia and LibGDX apis.
(recommended further reading: Model, View, Projection matrices in 3D graphics)
Next is rendering LibGDX 3D ModelInstances using Vuforia. A general solution to this problem, would be to convert the ModelInstance into something that Vuforia can recognize. This would be done by taking the mesh/vertex data represented by the LibGDX model, and feeding it directly into Vuforia.
IMO, the best way of going about this, would be to use a a core representation of the model data which can easily be given to both Vuforia and LibGDX (for example, a certain file format that both can recognize, or as raw FloatBuffers which should be easy to wrap up and give to either API). For reference, LibGDX stores models as vertex information in a collection of FloatBuffers, accessible through Model#meshes or ModelInstance#model#meshes.
Ok. So I finfally managed to combine both libraries. I´m not sure if what I´m doing is the most efficient way of working but it has worked for me.
First of all I´m basing myself on the Sample Apps from Vuforia. Specifically using the FrameMarkers example.
I opened an empty LibGDX project, imported the Vuforia jar and copied the SampleApplicationControl, SampleApplicationException, SampleApplicationGLView, SampleApplicationSession, FrameMarkerRenderer and FrameMarker.
Next, I created some attributes on the AndroidLauncher class of LibGDX and initialized all the Vuforia Stuff:
public class AndroidLauncher extends AndroidApplication implements SampleApplicationControl{
private static final String LOGTAG = "FrameMarkers";
// Our OpenGL view:
public SampleApplicationGLView mGlView;
public SampleApplicationSession vuforiaAppSession;
// Our renderer:
public FrameMarkerRenderer mRenderer;
MyGDX gdxRender;
// The textures we will use for rendering:
public Vector<Texture> mTextures;
public RelativeLayout mUILayout;
public Marker dataSet[];
public GestureDetector mGestureDetector;
public LoadingDialogHandler loadingDialogHandler = new LoadingDialogHandler(
this);
// Alert Dialog used to display SDK errors
private AlertDialog mErrorDialog;
boolean mIsDroidDevice = false;
#Override
protected void onCreate (Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
vuforiaAppSession = new SampleApplicationSession(this);
vuforiaAppSession.setmActivity(this);
AndroidApplicationConfiguration config = new AndroidApplicationConfiguration();
// Load any sample specific textures:
mTextures = new Vector<Texture>();
loadTextures();
startLoadingAnimation();
vuforiaAppSession.initAR(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
gdxRender = new MyGDX (vuforiaAppSession);
gdxRender.setTextures(mTextures);
initialize(gdxRender, config);
mGestureDetector = new GestureDetector(this, new GestureListener());
mIsDroidDevice = android.os.Build.MODEL.toLowerCase().startsWith(
"droid");
}
I needed to set the activity so I created the setmActivity() on the SampleApplicationSession.
After that I implemented the Libgdx ApplicationAdapter class and passed the vuforiaAppSession as an attribute to access all the stuff I initialized.
public class MyGDX extends ApplicationAdapter {
ModelInstance modelInstanceHouse;
private AnimationController controller;
Matrix4 lastTransformCube;
// Constants:
static private float kLetterScale = 25.0f;
static private float kLetterTranslate = 25.0f;
// OpenGL ES 2.0 specific:
private static final String LOGTAG = "FrameMarkerRenderer";
private int shaderProgramID = 0;
private Vector<com.mygdx.robot.Texture> mTextures;
//SampleApplicationSession vuforiaAppSession;
PerspectiveCamera cam;
ModelBuilder modelBuilder;
Model model;
ModelInstance instance;
ModelBatch modelBatch;
static boolean render;
public SampleApplicationSession vuforiaAppSession;
public MyGDX ( SampleApplicationSession vuforiaAppSession){
super();
this.vuforiaAppSession = vuforiaAppSession;
}
The last important thing to keep in mind is the render() method. I based myself on the the render method of the FrameMarkerRenderer. It has a boolean that gets activated when the camera starts. So I simple changed the variable both on the vuforia AR initialization and the render() method. I had to put the camera on and identity matrix and multiply the model by the modelViewMatrix.
#Override
public void render() {
if (render) {
// Clear color and depth buffer
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT);
// Get the state from Vuforia and mark the beginning of a rendering
// section
State state = Renderer.getInstance().begin();
// Explicitly render the Video Background
Renderer.getInstance().drawVideoBackground();
GLES20.glEnable(GLES20.GL_DEPTH_TEST);
// We must detect if background reflection is active and adjust the
// culling direction.
// If the reflection is active, this means the post matrix has been
// reflected as well,
// therefore standard counter clockwise face culling will result in
// "inside out" models.
GLES20.glEnable(GLES20.GL_CULL_FACE);
GLES20.glCullFace(GLES20.GL_BACK);
cam.update();
modelBatch.begin(cam);
if (Renderer.getInstance().getVideoBackgroundConfig().getReflection() == VIDEO_BACKGROUND_REFLECTION.VIDEO_BACKGROUND_REFLECTION_ON)
GLES20.glFrontFace(GLES20.GL_CW); // Front camera
else
GLES20.glFrontFace(GLES20.GL_CCW); // Back camera
// Set the viewport
int[] viewport = vuforiaAppSession.getViewport();
GLES20.glViewport(viewport[0], viewport[1], viewport[2], viewport[3]);
// Did we find any trackables this frame?
for (int tIdx = 0; tIdx < state.getNumTrackableResults(); tIdx++)
{
// Get the trackable:
TrackableResult trackableResult = state.getTrackableResult(tIdx);
float[] modelViewMatrix = Tool.convertPose2GLMatrix(
trackableResult.getPose()).getData();
// Choose the texture based on the target name:
int textureIndex = 0;
// Check the type of the trackable:
assert (trackableResult.getType() == MarkerTracker.getClassType());
MarkerResult markerResult = (MarkerResult) (trackableResult);
Marker marker = (Marker) markerResult.getTrackable();
textureIndex = marker.getMarkerId();
float[] modelViewProjection = new float[16];
Matrix.translateM(modelViewMatrix, 0, -kLetterTranslate, -kLetterTranslate, 0.f);
Matrix.scaleM(modelViewMatrix, 0, kLetterScale, kLetterScale, kLetterScale);
Matrix.multiplyMM(modelViewProjection, 0, vuforiaAppSession.getProjectionMatrix().getData(), 0, modelViewMatrix, 0);
SampleUtils.checkGLError("FrameMarkers render frame");
cam.view.idt();
cam.projection.idt();
cam.combined.idt();
Matrix4 temp3 = new Matrix4(modelViewProjection);
modelInstanceHouse.transform.set(temp3);
modelInstanceHouse.transform.scale(0.05f, 0.05f, 0.05f);
controller.update(Gdx.graphics.getDeltaTime());
modelBatch.render(modelInstanceHouse);
}
GLES20.glDisable(GLES20.GL_DEPTH_TEST);
modelBatch.end();
}
It´s a lot of code, but I hope it helps the people that try to start integrating both libraries. I don´t think this is efficient but it´s the only solution I´ve come with.
Pablo's answer is pretty good, however, if you are interested in a bit different approach (Calling Vuforia from LibGDX, not the other way) and a more complete example, here is a github repo with a simple 3D model renderer.
I loaded a 3D model from asset Manager and add CharacterControl as a control. every thing was working correctly but when I tried to rotate the model it did`nt work.
private CharacterControl player;
private Spatial model;
public static final Quaternion YAW045 = new Quaternion().fromAngleAxis(FastMath.PI/4, new Vector3f(0,1,0));
#Override
public void simpleInitApp() {
// add bullet app sate to state manager
bulletAppState = new BulletAppState();
stateManager.attach(bulletAppState);
bulletAppState.getPhysicsSpace().enableDebug(assetManager);
this.addModel();
}
private void addModel(){
model = assetManager.loadModel("Models/Oto/Oto.mesh.j3o");
model.setLocalTranslation(new Vector3f(0,10,0));
capsuleShape = new CapsuleCollisionShape(1f, 7.9f, 1);
player = new CharacterControl(capsuleShape, 1f);
bulletAppState.getPhysicsSpace().add(player);
model.addControl(player);
rootNode.attachChild(model);
model.rotate(YAW045);
}
please help me.
Richard is right.
The CharacterControl class has a setViewDirection() method.
You should really be switching to BetterCharacterControl though as it has better integration. I don't know why CharacterControl is not deprecated.
In general physics objects has a separate "life" as they live in the Bullet PhysicsSpace. For instance the other common physics control: RigidBodyControl class has a setPhysicsRotation(Quaternion rotation) method (just like it has a setPhysicsLocation() method).
More info is in the wiki (although it references CharacterControl) :
Walking Character
I created an AssetManger Class called AssetsTest.
package com.program.mydemo;
import com.badlogic.gdx.assets.AssetManager;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.TextureAtlas;
public class AssetsTest {
public static AssetManager manager = new AssetManager();
public static void load() {
manager.load("bgscreen.png", Texture.class);
manager.load("menu.pack", TextureAtlas.class);
}
public static void unload() {
manager.unload("bgscreen.png");
manager.unload("menu.pack");
}
public static boolean update(){
return manager.update();
}
public static void dispose() {
manager.dispose();
}
}
I also created another Class called MyDemo
public class MyDemo extends Game {
private SpriteBatch batch;
private Sprite spriteStart1, spriteStart2;
private Texture texture1, texture2;
private TextureAtlas atlas1, atlas2;
private TextureRegion startRegion1, startRegion2;
private Camera camera;
private int refCount;
public void create () {
batch=new SpriteBatch();
camera = new OrthographicCamera(500,700);
AssetsTest.load();
AssetsTest.manager.finishLoading();
texture1 = AssetsTest.manager.get("bgscreen.png",Texture.class);
atlas1 = AssetsTest.manager.get("menu.pack", TextureAtlas.class);
startRegion1 = atlas1.findRegion("startbutton");
spriteStart1 = new Sprite(startRegion1);
spriteStart1.setPosition(-210/2,-150);
texture2 = AssetsTest.manager.get("bgscreen.png",Texture.class);
atlas2 = AssetsTest.manager.get("menu.pack", TextureAtlas.class);
startRegion2 = atlas2.findRegion("startbutton");
spriteStart2 = new Sprite(startRegion2);
spriteStart2.setPosition(-210/2,-150);
refCount = AssetsTest.manager.getReferenceCount("bgscreen.png");
System.out.println(refCount);
// AssetsTest.unload();
// AssetsTest.load();
// AssetsTest.manager.finishLoading();
public void render () {
Gdx.gl.glClearColor(0,0,0,1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
batch.setProjectionMatrix(camera.combined);
batch.begin();
batch.draw(texture2, -500/2,-700/2);
spriteStart2.draw(batch);
batch.end();
}
#Override
public void dispose () {
super.dispose();
batch.dispose();
texture1.dispose();
AssetsTest.dispose();
}
}
Then, I created two Texture References (texture1 & texture2) reference to AssetsTest.manager.get(...);
The refCount is 1. Why?
It should be "2" because I have two references (texture1 & texture2).
If I try to call AssetsTest.load(); twice, I will get refCount is 2. Calling twice of AssetsTest.load(); should create two objects and stored in different memory address. Is my concept correct?
Directly from the libgdx documentation:
Assets are reference counted. If two assets A and B both depend on another
asset C, C won't be disposed until A and B have been disposed.
This also means that if you load an asset multiple times, it will actually be shared and only
take up memory once!
you load
texture1 = AssetsTest.manager.get("bgscreen.png",Texture.class);
and then
texture2 = AssetsTest.manager.get("bgscreen.png",Texture.class);
both texture1 and texture2 point to the same texture which is shared and takes memory only once. That's why you get ref count 1.
Update
Yes, texture1 and texture2 point to the same object ("bgscreen") which is shared. So in a sense they are reference variables pointing to the same memory address. Once an item is loaded in the asset manager it could be reused/shared in this way. In your case this means that no new object will be created each time you reference "bgscreen.png". So you will always get ref count 1.
As regards to the the 2nd part of your question. To clarify some things: Assetmanager.load in its way doesn't load any assets. The method just enqueues the assets. Once you call manager.update() or manager.isFinished only then the assets which you've enqueued with the load method will be loaded into memory. So if you call load twice of the same texture then when you call manager.update() you will end up with two references of the same object ("bgscreen.png") which is unnecessary because what you usually want is have only one shared reference of the same object for efficiency.
If this answers your question please don't forget to accept the response.
I am looking at a code sample that explains the proxy pattern. Here is the code:
/**
* Proxy
*/
public class ImageProxy implements Image {
/**
* Private Proxy data
*/
private String imageFilePath;
/**
* Reference to RealSubject
*/
private Image proxifiedImage;
public ImageProxy(String imageFilePath) {
this.imageFilePath= imageFilePath;
}
#Override
public void showImage() {
// create the Image Object only when the image is required to be shown
proxifiedImage = new HighResolutionImage(imageFilePath);
// now call showImage on realSubject
proxifiedImage.showImage();
}
}
/**
* RealSubject
*/
public class HighResolutionImage implements Image {
public HighResolutionImage(String imageFilePath) {
loadImage(imageFilePath);
}
private void loadImage(String imageFilePath) {
// load Image from disk into memory
// this is heavy and costly operation
}
#Override
public void showImage() {
// Actual Image rendering logic
}
}
/**
* Image Viewer program
*/
public class ImageViewer {
public static void main(String[] args) {
// assuming that the user selects a folder that has 3 images
//create the 3 images
Image highResolutionImage1 = new ImageProxy("sample/veryHighResPhoto1.jpeg");
Image highResolutionImage2 = new ImageProxy("sample/veryHighResPhoto2.jpeg");
Image highResolutionImage3 = new ImageProxy("sample/veryHighResPhoto3.jpeg");
// assume that the user clicks on Image one item in a list
// this would cause the program to call showImage() for that image only
// note that in this case only image one was loaded into memory
highResolutionImage1.showImage();
// consider using the high resolution image object directly
Image highResolutionImageNoProxy1 = new HighResolutionImage("sample/veryHighResPhoto1.jpeg");
Image highResolutionImageNoProxy2 = new HighResolutionImage("sample/veryHighResPhoto2.jpeg");
Image highResolutionImageBoProxy3 = new HighResolutionImage("sample/veryHighResPhoto3.jpeg");
// assume that the user selects image two item from images list
highResolutionImageNoProxy2.showImage();
// note that in this case all images have been loaded into memory
// and not all have been actually displayed
// this is a waste of memory resources
}
}
Assume proxy pattern is implemented correctly, and this is the main method of the program. Here is what i wonder: The comments in the code say that when we use proxy image objects, if we load a picture into memory, just that image is loaded. But if we do not use proxy and directly create real images, when we load an instance of this class, we load all instances of the class into memory. I do not understand why this is the case. Yes, the whole point of proxy pattern is to do this, but i do not understand why all 3 of the highResolutionImageNoProxy objects are loaded into memory when we call highResolutionImageNoProxy2.showImage(); . Can anyonbody explain it?
Thanks
Edit: I think i figured out why. Because the ImageProxy class calls the constructor of HighResolutionImage class only when it tries to do an operation on the object, but if we create a HighResolutionImage directly, then since its constructor creates the object, all of them are loaded into memory.
The code assumes that when you create an instance of HighResolutionImage, the image is loaded to the memory, even if showImage() isn't called.
The proxy will assure that the image is loaded to the memory only when showImage() is called.
//load veryHighResPhoto1 to memory
Image highResolutionImageNoProxy1 = new HighResolutionImage("sample/veryHighResPhoto1.jpeg");
//load veryHighResPhoto2 to memory
Image highResolutionImageNoProxy2 = new HighResolutionImage("sample/veryHighResPhoto2.jpeg");
//load veryHighResPhoto3 to memory
Image highResolutionImageBoProxy3 = new HighResolutionImage("sample/veryHighResPhoto3.jpeg");
//load just the proxys (image not loaded yet)
Image highResolutionImage1 = new ImageProxy("sample/veryHighResPhoto1.jpeg");
Image highResolutionImage2 = new ImageProxy("sample/veryHighResPhoto2.jpeg");
Image highResolutionImage3 = new ImageProxy("sample/veryHighResPhoto3.jpeg");
//trigger the load of the image into memory
highResolutionImage1.showImage();