I am developing a simple application using libgdx and the Bullet API. I have completed the basic prototype of this application using shapes but now I want to load the real models in the application. I have used the .g3db file for loading the models through AssetManager
private void doneLoading() {
// TODO Auto-generated method stub
Model model = manager.get("ping1.g3db", Model.class);
int index = 0;
for (int i = 0; i < model.nodes.size; i++) {
String id = model.nodes.get(i).id;
ModelInstance instance = new ModelInstance(model, id);
Node node = instance.getNode(id);
instance.transform.set(node.globalTransform);
//node.translation.set(0, 0, 0);
//node.scale.set(1, 1, 1);
//node.rotation.idt();
instance.calculateTransforms();
if (id.equals("ball4") || id.equals("bat")) {
instance_array.add(instance);
index += 1;
}
if (id.equals("ball4")) {
ball = instance;
Gdx.app.log("Ball Index", " " + index);
instance_map.put("ball", new GameObject.Constructor(model, id, new btSphereShape(0.165f), 1.0f));
createBall();
//ball.transform.setToTranslation(0f, 10f, 0f);
} else if (id.equals("bat")) {
Gdx.app.log("Bat Index", " " + index);
bat = instance;
instance_map.put("bat",new GameObject.Constructor(model, id, new btSphereShape(1.897f), 0.0f));
createBat();
}
}
loading = false;
}
private void createBat() {
// TODO Auto-generated method stub
GameObject object=instance_map.get("bat").construct();
object.body.setCollisionFlags(object.body.getCollisionFlags()|btCollisionObject.CollisionFlags.CF_CUSTOM_MATERIAL_CALLBACK);
instances_gameobject.add(object);
dynamicWorld.addCollisionObject(object.body);
}
private void createBall() {
// TODO Auto-generated method stub
GameObject object=instance_map.get("ball").construct();
Gdx.app.log("Ball", "Ball created");
object.moving=true;
object.body.setWorldTransform(object.transform);
object.body.setCollisionFlags(object.body.getCollisionFlags()|btCollisionObject.CollisionFlags.CF_CUSTOM_MATERIAL_CALLBACK);
instances_gameobject.add(object);
dynamicWorld.addCollisionObject(object.body);
}
Please tell how could I use the custom models for collision detection.
In the ConvexHullTest you can see how you can create a Shape for your CollisionObject from a Mesh.
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;
}
For me, it helped to "bake" the scales in blender per this post
Scaling a ModelInstance in libgdx 3D and Bullet engine
After doing this, collision shapes worked for me with this method.
Related
I'm doing a project right now that is supposed to be a math teaching app for kids.
I'm using firebase as my database to save the user names and password and also to save the questions and their answers.
I have a problem retreiving the question and the answers from the database and display it on the buttons and textview because it appears that the listener for single value event is triggered only when the class is done with all the other commands and i need to use this listener for every question i have.
My database:
My code so far:
ArrayList <Integer> list = new ArrayList<Integer>();
for (int i=1; i<11; i++) {
list.add(new Integer(i));
}
Collections.shuffle(list);
for (int i=0; i<5; i++) {
ref2 = ref.child(list.get(i).toString());
Questions.cont = false;
getQuestionsFromDb(ref2);
questionView.setText(Questions.question);
button1.setText(Questions.ans1);
button2.setText(Questions.ans2);
button3.setText(Questions.ans3);
button4.setText(Questions.ans4);
button1.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
// TODO Auto-generated method stub
Questions.cont = true;
int ans = getAns(Questions.question);
Button b = (Button)v;
int buttonText = Integer.parseInt(b.getText().toString());
if(ans == buttonText) {
Questions.points++;
}
}
});
}
questionView.setText(Questions.points);
// end of for loop
}
private void getQuestionsFromDb(Firebase ref2) {
// TODO Auto-generated method stub
ref2.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot snapshot) {
System.out.println("aaa");
String question = (String) snapshot.child("question").getValue();
String ans1 = snapshot.child("ans1").getValue().toString();
String ans2 = snapshot.child("ans2").getValue().toString();
String ans3 = snapshot.child("ans3").getValue().toString();
String ans4 = snapshot.child("ans4").getValue().toString();
Questions.question = question;
Questions.ans1 = ans1;
Questions.ans2 = ans2;
Questions.ans3 = ans3;
Questions.ans4 = ans4;
}
#Override
public void onCancelled(FirebaseError arg0) {
// TODO Auto-generated method stub
}
});
}
private int getAns(String ansString) {
// TODO Auto-generated method stub
String[] parts = ansString.split("\\b");
String ans;
if(parts.length == 5) {
ans = parts[0] + parts[1] + parts[2] + parts [3] + parts [4];
}
else {
ans = parts[0] + parts[1] + parts[2];
}
return Integer.parseInt(ans);
}
As you can see i'm also using outer class that have static variables in order to save the questions and the answers and use them out of the inner class.
I hope someone can help.
In your case you should add more listeners addListenerForSingleValueEvent() to get all the data you need or rewrite your logic using addValueEventListener. In that case your data will be actual at any time.
I am implementing the 'Dijkstra algorithm' in Android for making an app for Metro Network.
When I click on the button to show the path, it stops the app.
I have explicitly defined the graph to store in array adj[][].
MetroRoute class
public class MetroRoute extends Activity implements OnClickListener{
EditText source, destination;
TextView Route;
Button button;
#Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
setContentView(R.layout.metroroute);
source = (EditText) findViewById(R.id.etSource);
destination = (EditText) findViewById(R.id.etDest);
button = (Button) findViewById(R.id.bGetroute);
Route = (TextView) findViewById(R.id.tvRoute);
button.setOnClickListener(this);
}
#Override
public void onClick(View v) {
// TODO Auto-generated method stub
int count, i;
count = findpath(Integer.parseInt(source.getText().toString()), Integer.parseInt(destination.getText().toString()), path, sdist );
if(sdist!=0)
{
Route.setText("Shortest distance is : \n" + sdist);
Route.setText("Shortest Path is : ");
for( i=count;i>1;i--)
Route.setText(path[i] + " -> ");
Route.setText(path[i]);
}
else
Route.setText("There is no path from source to destination node\n");
}
int adj[][] = {{3,2,3,0,5},{1,2,3,4,5},{1,2,3,4,5},{1,2,3,4,5},{1,2,3,4,5}};
int MAX = 10, TEMP = 0, PERM =1, INFINITY = 9999, path[], sdist=0, n=5 ;
private class Node {
int predecessor[];
int dist[]; /*minimum distance of node from source*/
int status[];
}
int findpath(int s,int d,int path[],int sdist)
{
Node state = new Node();
int i, min=0, count=0, current, newdist, u, v ;
sdist=0;
/* Make all nodes temporary */
for(i=1;i<=n;i++)
{
state.predecessor[i]=0;
state.dist[i] = INFINITY;
state.status[i] = TEMP;
}
/*Source node should be permanent*/
state.predecessor[s]=0;
state.dist[s] = 0;
state.status[s] = PERM;
/*Starting from source node until destination is found*/
current=s;
while(current!=d)
{
for(i=1;i<=n;i++)
{
/*Checks for adjacent temporary nodes */
if ( adj[current][i] > 0 && state.status[i] == TEMP )
{
newdist=state.dist[current] + adj[current][i];
/*Checks for Relabeling*/
if( newdist < state.dist[i] )
{
state.predecessor[i] = current;
state.dist[i] = newdist;
}
}
}
/*Search for temporary node with minimum distance make it current node*/
min=INFINITY;
current=0;
for(i=1;i<=n;i++)
{
if(state.status[i] == TEMP && state.dist[i] < min)
{
min = state.dist[i];
current=i;
}
}
if(current==0) /*If Source or Sink node is isolated*/
return 0;
state.status[current]=PERM;
}
/* Getting full path in array from destination to source */
while( current!=0 )
{
count++;
path[count]=current;
current=state.predecessor[current];
}
/*Getting distance from source to destination*/
for(i=count;i>1;i--)
{
u=path[i];
v=path[i-1];
sdist+= adj[u][v];
}
return (count) ;
}
}
The easiest way to find the problem is to use the console of the adk to see the exceptions thrown by the device. When you start your app from your IDE (Eclipse, Android Studio) in a virtual device, or even your smartphone, you will get logcat-messages, which will contain the exceptions like for example the NullPointerException. You will then find the lines where the exception is thrown and then you can do some further testing (like writing some values to the console (System.Out.println(...) or System.err.println(...)).
You can post your log so we can take a look at it.
You may want to use my Dijkstra-implementation, too:
public static ArrayList<Integer> dijkstra(Graph graph, Integer orig,
Integer dest, TreeMap<Integer, Integer> distance,
ArrayList<Integer> nodesLeft) throws Exception
{
Iterator<Integer> iterator = graph.getNodes().iterator();
Integer next;
TreeMap<Integer, ArrayList<Integer>> paths = new TreeMap<Integer, ArrayList<Integer>>();
while (iterator.hasNext()) {
next = iterator.next();
distance.put(next, -1);
paths.put(next, new ArrayList<Integer>());
}
distance.put(orig, 0);
nodesLeft.addAll(Collections.unmodifiableCollection(graph.getNodes()));
while (!nodesLeft.isEmpty()) {
int u = nodesLeft.get(0);
Collection<Integer> edgesOfU = graph.getEdges(u);
Iterator<Integer> itEdgesOfU = edgesOfU.iterator();
int nextEdge;
while (itEdgesOfU.hasNext()) {
nextEdge = itEdgesOfU.next();
if (nodesLeft.contains(nextEdge)) {
int distU = distance.get(u);
if ((distance.get(nextEdge) == -1)
|| (distU + 1) < distance.get(nextEdge)) {
distance.put(nextEdge, distU + 1);
ArrayList<Integer> tmpList = paths.get(nextEdge);
for (int a : paths.get(u)) {
tmpList.add(a);
}
tmpList.add(nextEdge);
}
}
}
nodesLeft.remove(0);
}
return paths.get(dest);
}
I used some self-defined Graphs with the following Interface:
import java.util.Collection;
public interface Graph {
public Integer addNode();
public void removeNode(Integer id) throws Exception,
UnsupportedOperationException;
public Collection<Integer> getNodes();
public boolean addEdge(Integer orig, Integer dest) throws Exception;
public boolean removeEdge(Integer orig, Integer dest) throws Exception,
UnsupportedOperationException;
public Collection<Integer> getEdges(Integer orig) throws Exception;
}
I downloaded a class from Catch The Cows, it is akin to a Google Map object or at least that is what I am using it for.
It parses an XML file which lists the areas of the screen that should be touchable, and then creates them with this method.
This is here for context, I have commented out some parts of code, and added my own to try and resolve my issue
private Area addShape( String shape, String name, String coords, String id) {
Log.v("IDS:", "id was "+id);
Area a = null;
String rid = id.replace("#+id/", "");
Log.v("IDS:", "rid was "+rid);
// Generate a new ID for the area.
int _id = 1;
View vi = findViewById(_id);
while (vi!=null) {
_id++;
vi = findViewById(_id);
}
//View.generateViewId(); //=0;
Log.v("IDS:", "After conversion final time "+_id);
/*
try {
Class<R.id> res = R.id.class;
Field field = res.getField(rid); // eg. rid = area10
_id = field.getInt(null);
Log.v("IDS:", "After conversion "+_id);
}
catch (Exception e) {
_id = 0;
Log.e("Exception ",e.getMessage());
} finally {
Log.v("IDS:", "After conversion final time "+_id);
}
*/
if (_id != 0) {
if (shape.equalsIgnoreCase("rect")) {
String[] v = coords.split(",");
if (v.length == 4) {
a = new RectArea(_id, name, Float.parseFloat(v[0]),
Float.parseFloat(v[1]),
Float.parseFloat(v[2]),
Float.parseFloat(v[3]));
}
}
if (shape.equalsIgnoreCase("circle")) {
String[] v = coords.split(",");
if (v.length == 3) {
a = new CircleArea(_id,name, Float.parseFloat(v[0]),
Float.parseFloat(v[1]),
Float.parseFloat(v[2])
);
}
}
if (shape.equalsIgnoreCase("poly")) {
a = new PolyArea(_id,name, coords);
}
if (a != null) {
addArea(a);
}
} else {
Log.v("Loading ID: ","_id was 0");
}
return a;
}
Unfortunately nothing was rendering on the screen, and this was because _id = 0. This should be changed with this bit of code:
try {
Class<R.id> res = R.id.class;
Field field = res.getField(rid); // eg. rid = area10
_id = field.getInt(null);
}
How ever I am not sure what it does to try and debug it, can anyone explain what this snippet is doing?
R is a Read-Only class. It is generate at compile time and You should not use reflection to modify its field. Also you should avoid reflection to access the fields values. You should use the official API.
The comment at the first row of the class is
/* AUTO-GENERATED FILE. DO NOT MODIFY. */
Im new to android dev and using andengine and ive just come across a problem when dealing with a large animation that covers more then 1 sprite sheet. Basically I have a large sprite whose animation runs across 2 sprite sheets. Im trying to find a way to load them successfully. I will show you what I am trying and hopefully some one can either show me the correct way or help me finish it my way.
i start off by creating the 2 texture packs from the xml files.
these are created fine
TexturePackTextureRegionLibrary packer1 = null,packer2 = null;
TexturePack spritesheetTexturePack1 = null,spritesheetTexturePack2 = null;
try {
spritesheetTexturePack1 = new TexturePackLoader(activity.getTextureManager(), "Animation/Jack/")
.loadFromAsset(activity.getAssets(), "Jack_walk1" + ".xml");
spritesheetTexturePack1.loadTexture();
packer1 = spritesheetTexturePack1.getTexturePackTextureRegionLibrary();
} catch (final TexturePackParseException e) {
Debug.e(e);
}
TexturePackerTextureRegion textureRegion = packer1.get(Jack_walk1.LOOP_JACK_WALK_TO_SAFE_AREA_00000_ID);
try {
spritesheetTexturePack2 = new TexturePackLoader(activity.getTextureManager(), "Animation/Jack/")
.loadFromAsset(activity.getAssets(), "Jack_walk2" + ".xml");
spritesheetTexturePack2.loadTexture();
packer2 = spritesheetTexturePack2.getTexturePackTextureRegionLibrary();
} catch (final TexturePackParseException e) {
Debug.e(e);
}
TexturePackerTextureRegion textureRegion2 = packer2.get(Jack_walk1.LOOP_JACK_WALK_TO_SAFE_AREA_00000_ID);
ArrayList<SparseArray> animList = new ArrayList<SparseArray>();
animList.add(packer1.getIDMapping());
animList.add(packer2.getIDMapping());
TiledTextureRegion text1 = TiledTextureRegion.create(textureRegion.getTexture(), (int) textureRegion.getTextureX(), (int) textureRegion.getTextureY(), animList);
I then added this function to the tiledtextureregion to take in a list of arrays that hold the frame information and step through adding them to the itexturregion array
public static TiledTextureRegion create(final ITexture pTexture, final int pTextureX, final int pTextureY, final ArrayList<SparseArray> animList) {
ITextureRegion[] textureRegions = null;
int maxFrame = 0;
for(int i = 0; i < animList.size(); i++){
maxFrame += animList.get(i).size();
}
int currentFrame = 0;
textureRegions = new ITextureRegion[maxFrame];
for(int i = 0 ; i < animList.size(); i++){
SparseArray<? extends ITexturePackTextureRegion> packer = animList.get(i);
for(int j = 0; j < packer.size(); j++) {
if (packer.valueAt(j)!= null){
final int x = (int) packer.valueAt(j).getTextureX();
final int y = (int) packer.valueAt(j).getTextureY();
final int width = packer.valueAt(j).getSourceWidth();
final int height = packer.valueAt(j).getSourceHeight();
final Boolean rotated = packer.valueAt(j).isRotated();
textureRegions[currentFrame] = new TextureRegion(pTexture, x, y, width, height, rotated);
currentFrame++;
}
}
}
return new TiledTextureRegion(pTexture, false, textureRegions);
}
but the line return new TiledTextureRegion(pTexture, false, textureRegions); is expecting 1 texture do retrieve the frames from when creating the tiled region. Any ideas where i should go from here or is there a super easy way to handle this that i have over looked. Thanks for any help
This class is designed to work with a single texture.
If you cannot merge your textures into one (which I think is the case), then you can try to write a new class implementing ITextureRegion that will contain 2 or more TiledTextureRegion objects, and which will have a method to select one of these at will.
You will just have to delegate the remaining methods of ITextureRegion to the selected object.
public class MultipleTextureRegion implements ITextureRegion {
private List<TiledTextureRegion> internal;
private int selected=0;
//...
public void add(TiledTextureRegion region) {
internal.add(region);
}
public void select(int index) {
selected=index;
}
//...
// Delegates all ITextureRegion methods ...
public int getWidth() { return internal.get(selected).getWidth(); }
// And so on...
}
I've been trying to figure this out for a while now... I need to place marks over top of a seekBar to show the user places that they bookmarked in the past. The data is stored in xml. The problem is making the little ovals appear over the seekBar... It just doesn't work...
Here's my code:
public class seekMark extends View {
private int seekLength; // in pixels
private int seekLeftPad; // in pixels
private int seekBottomPad; // in pixels
private int trackLength; // in ms
private float pxOverMs; // in px/ms
ShapeDrawable lmark;
private seekMark instance;
public seekMark(Context context){
super(context);
instance = this;
seekLength = progressBar.getWidth();
seekLeftPad = progressBar.getPaddingLeft();
seekBottomPad = progressBar.getBottom();
trackLength = player.getDuration();
pxOverMs = pxPerMs();
lmark = new ShapeDrawable(new OvalShape());
}
private float pxPerMs(){
return ((float) seekLength)/((float) trackLength);
}
private int[] markPxList() throws XmlPullParserException, IOException {
int bmStartTime = 0;
String bmNames[] = bmNameList(xmlPath);
int[] bmPos = new int[bmNames.length];
for(int i=0; i < bmNames.length; i++){
bmStartTime = getBookmark(xmlPath, bmNames[i]);
bmPos[i] = (int) (bmStartTime * pxOverMs);
}
return (bmPos);
}
public void markPlace() throws XmlPullParserException, IOException {
int y = seekBottomPad;
int x = 0;
int bmPos[] = markPxList();
for(int i = 0; i < bmPos.length; i++){
x = bmPos[i] + seekLeftPad;
lmark = new ShapeDrawable();
lmark.getPaint().setColor(0xff74AC23);
lmark.setBounds(x, y, x + 1, y + 1);
instance.invalidate();
}
}
protected void onDraw(Canvas canvas) {
lmark.draw(canvas);
}
}
It's called from onCreate using this code. I call it using in another thread to avoid the problem where the dimensions of progressBar aren't yet set in onCreate.
Display display = ((WindowManager) getSystemService(WINDOW_SERVICE)).getDefaultDisplay();
if (display.getRotation() == 1){ // if landscape
final Runnable runner = new Runnable() {
public void run() {
seekMark seekMarks = new seekMark(context);
try {
seekMarks.markPlace();
} catch (XmlPullParserException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
// runs in another thread to avoid the problem with calling
// seekMark directly from onCreate
}
};
handler.postDelayed(runner, 1000);
}
The program crashes whenever I try to call seekMark.markPlace()... I'm trying to draw this over top of my layout main.xml.
im not sure if this is what you are trying to do.
Customize Seekbar
this seems to be similar while the approach is different.