package com.example.standardbenutzer.adelpath;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.os.Environment;
import android.support.v7.app.ActionBarActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.SurfaceHolder;
import java.io.File;
import java.io.FileOutputStream;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.TimeZone;
public class MainActivity extends ActionBarActivity implements SurfaceHolder.Callback {
private SurfaceHolder mHolder;
private MyThread mRunningThread;
private DrawingView mView;
private Renderer mRender;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mView = (DrawingView) findViewById(R.id.surface);
mHolder = mView.getHolder();
mHolder.addCallback(this);
}
@Override
public void surfaceCreated(SurfaceHolder holder) {
Log.e("Info-->", "Surface Created.");
if (mRunningThread == null) {
Log.e("Info-->", "Running thread was null.");
Canvas cv = holder.lockCanvas();
mRender = new Renderer(new Scene(), cv, new Vector3D(0.0f, 0.0f, 10.0f), 4);
mRunningThread = new MyThread(mView, holder, mRender);
mRunningThread.start();
holder.unlockCanvasAndPost(cv);
} else { //falls vom Homescreen wieder zurück in die App gewechselt wird
Log.e("Info-->", "Running thread was not null.");
createNewThread(false); //keinen neuen Renderer erstellen, da nur zurück in die App gewechselt wurde
Log.e("Info-->", "Running Thread Restart.");
}
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
//Passiert, wenn der Benutzer auf den Homescreen oder zu einer anderen App wechselt
Log.e("Info-->", "Surface was destroyed.");
mRunningThread.Stop();
Log.e("Info-->", "Running Thread Stop.");
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.menu_main, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
int id = item.getItemId();
if (id == R.id.action_exit) {
mRunningThread.Stop();
} else if (id == R.id.action_restart) {
createNewThread(true); //Neuen Renderer erstellen, da explizit ausgewählt
} else if (id == R.id.action_saveImage) {
saveImage();
}
return super.onOptionsItemSelected(item);
}
public void saveImage() {
Bitmap bitmap = mRender.GetBitmap();
try {
Date d = new Date();
SimpleDateFormat sdf = new SimpleDateFormat("hhmm ddMMyy");
sdf.setTimeZone(TimeZone.getTimeZone("Europe/Amsterdam"));
String dateString = sdf.format(d);
File file = new File(Environment.getExternalStorageDirectory(), "AdelPath_" + dateString + ".png");
FileOutputStream fos = new FileOutputStream(file);
bitmap.compress(Bitmap.CompressFormat.PNG, 100, fos);
//fos.close();
} catch (Exception e) {
Log.e("Error --->", e.toString());
}
}
public void createNewThread(boolean newRenderer) {
mRunningThread.Stop();
Canvas canv = mHolder.lockCanvas();
mRunningThread = null;
if (newRenderer) { //nur wenn explizit ausgewählt
mRunningThread = new MyThread(mView, mHolder, new Renderer(new Scene(), canv, new Vector3D(0.0f, 0.0f, 10.0f), 4));
mRunningThread.start();
} else { //es wird mit dem alten Renderer weitergerendert, so geht der Stand nicht verlohren
mRunningThread = new MyThread(mView, mHolder, mRender);
mRunningThread.start();
}
mHolder.unlockCanvasAndPost(canv);
mView.invalidate();
}
}
package com.example.standardbenutzer.adelpath;
import android.graphics.Canvas;
import android.util.Log;
import android.view.SurfaceHolder;
/**
* Created by Standardbenutzer on 14.12.2014.
*/
public class MyThread extends Thread {
private SurfaceHolder mHolder;
private Renderer mRender;
private boolean mIsRunning = true;
private DrawingView mView;
public MyThread(DrawingView view, SurfaceHolder holder, Renderer render) {
mHolder = holder;
mView = view;
mRender = render;
}
@Override
public void run() {
while (mIsRunning) {
try {
if (mHolder != null) {
Canvas canvas = mHolder.lockCanvas();
if (canvas == null) {
Log.e("Info-->", "Canvas was null in MyThread.");
mHolder.unlockCanvasAndPost(canvas);
Log.e("Info-->", "Post canvas back to Holder.");
}
mRender.setmCanvas(canvas);
synchronized (mView.getHolder()) {
mRender.RenderOneStep();
}
mHolder.unlockCanvasAndPost(mRender.DisplayResult());
mView.invalidate();
Log.e("Info-->", "Computed Sample.");
}
sleep(350);
} catch (Exception e) {
Log.d("Error-->", "run " + e);
}
}
}
public void Stop() {
mIsRunning = false;
}
public void Restart() {
mIsRunning = true;
}
}
package com.example.standardbenutzer.adelpath;
import java.util.ArrayList;
/**
* Created by Standardbenutzer on 07.12.2014.
*/
public class Scene {
private static ArrayList<BaseObject> sObjectList = new ArrayList<BaseObject>();
public Scene() {
sObjectList.add(new Sphere(new CColor(255, 255, 255, 255), true, new Vector3D(0.0f, 0.0f, 0.0f), 1.5f, Material.Diffuse));
sObjectList.add(new Sphere(new CColor(255, 125, 0, 0), false, new Vector3D(-2.0f, -2f, 0.0f), 1.0f, Material.Diffuse));
sObjectList.add(new Sphere(new CColor(255, 255, 140, 0), false, new Vector3D(2.0f, 1.0f, 0.0f), 1.0f, Material.Diffuse));
sObjectList.add(new Plane(new CColor(255, 200, 200, 200), false, new Vector3D(0.0f, -0.5f, 0.0f), 1.0f, Material.Diffuse));
sObjectList.add(new Plane(new CColor(255, 200, 200, 200), false, new Vector3D(1.0f, 0f, 0.0f), -3.0f, Material.Diffuse));
sObjectList.add(new Plane(new CColor(255, 200, 200, 200), false, new Vector3D(1.0f, -1.0f, 0.0f), 1.0f, Material.Diffuse));
}
public static ArrayList<BaseObject> getObjectList() {
return sObjectList;
}
}
package com.example.standardbenutzer.adelpath;
import android.content.Context;
import android.graphics.Canvas;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
/**
* Created by Standardbenutzer on 07.12.2014.
*/
class DrawingView extends SurfaceView implements SurfaceHolder.Callback {
private SurfaceHolder mSurfaceHolder = this.getHolder();
private Canvas canvas;
private Vector3D Camera = new Vector3D(0.0f, 0.0f, 10f);
private int MAX_DEPTH = 2;
private int RAYS_PER_PIXEL = 1;
private Scene scene = new Scene();
public DrawingView(Context context) {
super(context);
this.setDrawingCacheEnabled(true);
mSurfaceHolder.addCallback(this);
}
public DrawingView(Context context, AttributeSet attrs) {
super(context, attrs);
this.setDrawingCacheEnabled(true);
mSurfaceHolder.addCallback(this);
}
public DrawingView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
this.setDrawingCacheEnabled(true);
mSurfaceHolder.addCallback(this);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
if (mSurfaceHolder.getSurface().isValid()) {
}
}
return false;
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
}
@Override
public void surfaceChanged(SurfaceHolder holder, int one, int two, int three) {
}
@Override
public void surfaceCreated(SurfaceHolder holder) {
}
}
--------
package com.example.standardbenutzer.adelpath;
/**
* Created by Standardbenutzer on 08.12.2014.
*/
public class CColor {
public float getA() {
return a;
}
public float getR() {
return r;
}
public float getG() {
return g;
}
public float getB() {
return b;
}
private float a;
private float r;
private float g;
public void setB(float b) {
this.b = b;
}
public void setA(float a) {
this.a = a;
}
public void setR(float r) {
this.r = r;
}
public void setG(float g) {
this.g = g;
}
private float b;
public CColor(float a, float r, float g, float b) {
this.a = a;
this.r = r;
this.g = g;
this.b = b;
}
public CColor(float r, float g, float b) {
this.a = 255;
this.r = r;
this.g = g;
this.b = b;
}
public CColor add(CColor color) {
float red = this.r += color.r;
float green = this.g += color.g;
float blue = this.b += color.b;
return new CColor(red, green, blue);
}
public CColor divide(float value) {
float red = this.r / value;
float green = this.g / value;
float blue = this.b / value;
return new CColor(red, green, blue);
}
}
package com.example.standardbenutzer.adelpath;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
/**
* Created by Standardbenutzer on 14.12.2014.
*/
public class Renderer {
private Scene mScene;
public Canvas getmCanvas() {
return mCanvas;
}
public void setmCanvas(Canvas mCanvas) {
this.mCanvas = mCanvas;
}
private Canvas mCanvas;
private Vector3D mCamera;
private int RAY_DEPTH;
private float[] accumulator;
private int samples = 0;
public Renderer(Scene scene, Canvas canvas, Vector3D Camera, int RAY_DEPTH) {
this.mScene = scene;
this.mCanvas = canvas;
this.mCamera = Camera;
this.RAY_DEPTH = RAY_DEPTH;
this.accumulator = new float[(canvas.getWidth() * canvas.getHeight()) * 3 + 1];
}
public void RestartProgressiveRendering() {
this.accumulator = new float[(mCanvas.getWidth() * mCanvas.getHeight()) * 3 + 1];
this.samples = 0;
}
public void RenderOneStep() {
int width = this.mCanvas.getWidth();
int height = this.mCanvas.getHeight();
int i = 0;
for (int x = 0; x < width; x++) {
for (int y = 0; y < height; y++) {
CColor cPixel = ComputeSample(x, y, width, height);
accumulator[i * 3 + 0] += cPixel.getR();
accumulator[i * 3 + 1] += cPixel.getG();
accumulator[i * 3 + 2] += cPixel.getB();
i++;
}
}
samples++;
}
public Canvas DisplayResult() {
int width = this.mCanvas.getWidth();
int height = this.mCanvas.getHeight();
Paint p = new Paint();
int i = 0;
for (int x = 0; x < width; x++) {
for (int y = 0; y < height; y++) {
int r = (int) (accumulator[i * 3 + 0] / (float) samples); //die gespeicherte Farbe wird durch die Anzahl der Samples geteilt
int g = (int) (accumulator[i * 3 + 1] / (float) samples);
int b = (int) (accumulator[i * 3 + 2] / (float) samples);
p.setColor(Color.rgb(r, g, b));
this.mCanvas.drawPoint(x, y, p);
i++;
}
}
Paint myPaint = new Paint();
myPaint.setColor(Color.WHITE);
mCanvas.drawText("Samples: " + String.valueOf(this.samples), 10, 25, myPaint);
return this.mCanvas;
}
//Liefert das Bitmap um das Renderergebnis speichern zu können
public Bitmap GetBitmap() {
int width = this.mCanvas.getWidth();
int height = this.mCanvas.getHeight();
Bitmap rBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
rBitmap.hasAlpha(); //hilft evtl. dass das Bild richtig gespeichert wird
int i = 0;
for (int x = 0; x < width; x++) {
for (int y = 0; y < height; y++) {
int r = (int) (accumulator[i * 3 + 0] / (float) samples);
int g = (int) (accumulator[i * 3 + 1] / (float) samples);
int b = (int) (accumulator[i * 3 + 2] / (float) samples);
rBitmap.setPixel(x, y, Color.rgb(r, g, b));
i++;
}
}
return rBitmap;
}
private CColor ComputeSample(int x, int y, int width, int height) {
float fov = 160.0f * (float) Math.PI / 180.0f;
float zdir = 1.0f / (float) Math.tan(fov);
float aspect = (float) height / (float) width;
float xdir = (x / (float) width) * 2.0f - 1.0f;
float ydir = ((y / (float) height) * 2.0f - 1.0f) * aspect;
// CColor color = new CColor(0.0f, 0.0f, 0.0f);
Vector3D direction = new Vector3D(xdir, ydir, zdir).normalize();
Ray ray = new Ray(mCamera, direction);
// for(int row = 0; row < 2; row++){
// for(int col = 0; col < 2; col++){
// mCamera.x = mCamera.x + (col + 0.5f) / 2.0f;
// mCamera.y = mCamera.y + (row + 0.5f) / 2.0f;
// Ray ray = new Ray(mCamera, direction);
// color.add(Trace(ray,1));
// }
// }
//
// return color.divide(4.0f);
return Trace(ray, 1);
}
public CColor Trace(Ray ray, int depth) {
float hitDistance = 5000.0f;
BaseObject hitObject = null;
for (BaseObject obj : Scene.getObjectList()) {
float intersect = obj.intersect(ray);
if (intersect < hitDistance && intersect > -1.0f) {
hitDistance = intersect;
hitObject = obj;
}
}
//kein Objekt getroffen
if (hitDistance == 5000.0f) {
return new CColor(0, 0, 0);
}
//das getroffene Objekt ist eine Lichtquelle
if (hitObject.isEmitter) {
return hitObject.color;
}
//die maximale Tiefe ist erreicht
if (depth == this.RAY_DEPTH) {
return new CColor(0, 0, 0);
}
Vector3D hitPoint = ray.origin.add(ray.direction.mult(hitDistance * 0.99f)); //Behebung der floating point precision mit *0.99f
Vector3D normal = hitObject.normal(hitPoint);
Ray reflectionRay = null;
if (hitObject.material == Material.Diffuse) {
Vector3D randomVector = Vector3D.getRandomVectorInHemisphere(); //zufälliger Punkt bei diffusem Material
if (randomVector.dot(normal) < 0.0f) //falls der Punkt in der falschen Hemisphere liegt
randomVector = randomVector.negate();
reflectionRay = new Ray(hitPoint, randomVector.normalize()); //Reflektionsvector
}
CColor returnColor = Trace(reflectionRay, depth + 1);
float r = hitObject.color.getR() * returnColor.getR();
float g = hitObject.color.getG() * returnColor.getG();
float b = hitObject.color.getB() * returnColor.getB();
r /= 255.0f;
g /= 255.0f;
b /= 255.0f;
return new CColor(r, g, b);
}
}
Das müssten alle Änderungen gewesen sein, die ich bis jetzt vorgenommen habe. Wenn du dir den Code genau ansiehst, wird dir auffallen, dass an vielen Stellen nicht mehr auf die Variablen dirket zugegriffen wird.
Aktuell hast du nämlich schon Probleme mit dem Scope und das nicht nur an 1-2 Stellen, sondern im ganzen Programm...
An die Mods: Es wäre hilfreich, wenn in diesem Thread eine Ausnahme bestehen würde was die externen Links angeht. Jedenfalls ist das meine Ansicht (muss ja niemand teilen^^)... Gleichzeitig kann ich es aber auch nachvollziehen, dass es natürlich ein Sicherheitsrisiko darstellt und wenn man es in einem Thread erlaubt ggf. in anderen Threads ebenfalls eine "Extrawurst" gewünscht wird... Daher geht meine Idee in Richtung "Leute für Links freischalten", oder eine temporäre Linkbereitstellung, oder dass AndroidPIT als Hoster für Textdateien* bis vll. 100KB bereitsteht o.Ä.
Per PM zu versenden wäre natürlich auch eine Möglichkeit, aber ehrlich gesagt finde ich keinen "Posteingang" bei mir (nur in den Mails und dort übersehe ich gerne mal was)
*wobei das mit den Textdateien wohl der Grundstein alles Übles sein dürfte, daher ist dieser Vorschlag wohl eher mit "vorsicht" zu genießen
— geändert am 22.12.2014, 18:55:40
Empfohlener redaktioneller Inhalt
Mit Deiner Zustimmung wird hier ein externer Inhalt geladen.
Mit Klick auf den oben stehenden Button erklärst Du Dich damit einverstanden, dass Dir externe Inhalte angezeigt werden dürfen. Dabei können personenbezogene Daten an Drittanbieter übermittelt werden. Mehr Infos dazu findest Du in unserer Datenschutzerklärung.