Какой фреймворк использовать для вытягивания 3D-объекта из сплайна

Я ищу способ создания 3D-объектов путем вращения сплайна вокруг оси Y на Android. В результате должен получиться предмет, похожий на миску/стакан:

http://www.3d-resources.com/cinema_4d_tutorials/realistic_glass_tutorial.html

Форма формы должна быть динамической, пользователь должен иметь возможность настраивать радиус и контрольные точки сплайна в приложении.

В основном я ищу самый простой способ реализовать это. Может ли кто-нибудь указать мне в правильном направлении? (Структура, примерная реализация,...)

Есть ли известное название для этой техники моделирования? (в синема 4д это называется "токарный нурбс")

----- РЕДАКТИРОВАТЬ -----

Мой ввод представляет собой последовательность 2D-контрольных точек (путь Безье) и радиус между осью вращения и сплайном. Моим выводом должен быть 3D-рендеринг объекта в реальном времени, который создается путем вращения сплайна вокруг оси.


person stoefln    schedule 23.11.2014    source источник
comment
Этот вопрос очень широк. Каковы ваши входы? Каковы ваши ожидаемые результаты?   -  person Reticulated Spline    schedule 24.11.2014
comment
Итак, вам на самом деле нужны две разные функции: одна функция для выполнения вращения сплайна вокруг оси, а другая функция для рендеринга указанного объекта. Это очень широкое требование. Что вы сделали до сих пор?   -  person Reticulated Spline    schedule 24.11.2014
comment
@ReticulatedSpline, ты прав. Я пока мало что сделал. Я играл с Rachawali Framework github.com/MasDennis/Rajawali, но должен сказать, что я не очень опытен в программировании 3D. Поэтому и спрашиваю в общих чертах. Я тоже искал в сети, но не смог найти многообещающего подхода.   -  person stoefln    schedule 24.11.2014


Ответы (1)


Информация о кривой Бузье и о поворот поверхности.

Вы можете взять код с github.
Я написал его несколько лет назад, когда мне было довольно новый в Java, поэтому качество кода может быть немного плохим...

Вывод: введите здесь описание изображения

Основные классы:

Кривая Безье:

package kpi.ua.shapes;

import java.awt.Graphics2D;
import java.util.LinkedList;
import java.util.List;

public class BezierCurve {

    private List<Point> allPoints;

    public BezierCurve(List<Point> allPoints) {
        this.allPoints = allPoints;
    }

    public Point getValue(double t) {
        Point result = new Point();
        int i = 0;
        Double x=new Double(0);
        Double y=new Double(0);
        for (Point p : allPoints) {
            x+=p.getX() * b(i, allPoints.size()-1, t);
            y+=p.getY() * b(i, allPoints.size()-1, t);
            i++;
        }
        result.setX(x);
        result.setY(y);
        return result;
    }

    private Double b(int i, int n, double t) {
        return fact(n)* Math.pow(t, i) * Math.pow(1 - t, n - i) / (fact(i) * fact(n - i));
    }

    private int fact(int num) {
        return (num == 0) ? 1 : num * fact(num - 1);
    }

    public void drawCarcass(Graphics2D gr) {
        Double[] x=new Double[allPoints.size()];
        Double[] y=new Double[allPoints.size()];
        int i=0;
        for(Point p:allPoints){
            x[i]=p.getX();
            y[i]=p.getY();
            i++;
        }
      //  gr.drawPolygon(x, y, i);
    }

    public List<Line> getCurve(){
        LinkedList<Line> curve=new LinkedList<Line>();
        Double t=0.0;
        Point prev = getValue(t);
        Point next; 
        while (t <= 1) {
            next =getValue(t);
            curve.add(new Line(prev,next));
           // gr.drawLine(prev.getX(), prev.getY(), next.getX(), next.getY());
            prev = next;
            t += 0.001;
        }
        next = getValue(t);
        curve.add(new Line(prev,next));
        return curve;
        //gr.drawLine(prev.getX(), prev.getY(), next.getX(), next.getY());   
    }
    public void drawCurve(Graphics2D gr){
//        Double t=0.0;
//        Point prev = getValue(t);
//        Point next; 
//        gr.setColor(Color.yellow);
//        gr.setStroke(new BasicStroke(2.5f));
//        while (t <= 1) {
//            next =getValue(t);          
//         //   gr.drawLine(prev.getX(), prev.getY(), next.getX(), next.getY());
//            prev = next;
//            t += 0.001;
//        }
//        next = getValue(t);
       // gr.drawLine(prev.getX(), prev.getY(), next.getX(), next.getY());        
    }


}

Сложный:

package kpi.ua.shapes;

public class Complex extends Point {

    private Double re;
    private Double im;

    public Complex(Double im, Double re) {
        this.im = im;
        this.re = re;
        Double r = Math.sqrt(im * im + re * re);
        Double fi = Math.atan(im / re);
        this.x=r * Math.cos(fi);
        this.y=r * Math.sin(fi);
    }

    public Double getRe() {
        return re;
    }

    public void setRe(Double re) {
        this.re = re;
    }

    public Double getIm() {
        return im;
    }

    public void setIm(Double im) {
        this.im = im;
    }

}

Конус:

package kpi.ua.shapes;

public class Cone {
    private Double a;
    private Double b;
    private Double c;
    private Double fi;

    public Cone() {
        super();
    }
    public Cone(Double a, Double b, Double c) {
        super();
        this.a = a;
        this.b = b;
        this.c = c;
    }
    public Double getA() {
        return a;
    }
    public void setA(Double a) {
        this.a = a;
    }
    public Double getB() {
        return b;
    }
    public void setB(Double b) {
        this.b = b;
    }
    public Double getC() {
        return c;
    }
    public void setC(Double c) {
        this.c = c;
    }
    public Double getFi() {
        return fi;
    }
    public void setFi(Double fi) {
        this.fi = fi;
    }
    public Point3D getPoint(Double fi,Double r){
        Double x=this.a*r*Math.cos(fi);
        Double y=this.b*r*Math.sin(fi);
        Double z=this.c*r;
        return new Point3D(x, y, z);
    }
}

Поверхность конуса:

package kpi.ua.shapes;

import java.awt.Graphics2D;
import java.util.LinkedList;
import java.util.List;

import kpi.ua.util.ControlUtil;
import kpi.ua.util.GraphicUtil;

public class ConeSurface {
    private List<Point3D> conePoints;
    public List<Point3D> getConePoints() {
        return conePoints;
    }
    public void setConePoints(List<Point3D> conePoints) {
        this.conePoints = conePoints;
    }

    public void calcConePoints(){
        Cone cone = new Cone(ControlUtil.CONE_PARAMETR_A, ControlUtil.CONE_PARAMETR_B, ControlUtil.CONE_PARAMETR_C);
        Double r = ControlUtil.CONE_START_RADIOUS;
        conePoints=new LinkedList<Point3D>();
        while (r >= 0) {
            for (double fi = 0; fi < 2 * Math.PI; fi += ControlUtil.STEP_BY_FI) {
                conePoints.add(cone.getPoint(fi, r));
                conePoints.add(cone.getPoint(fi+ControlUtil.STEP_BY_FI, r));
                if(r!=0){
                    conePoints.add(cone.getPoint(fi, r-1));
                }
            }
            r--;
        }
    }
    public void drawCone(Graphics2D gr){
        if(conePoints==null)
            calcConePoints();
        GraphicUtil.drawTriangulation(gr, conePoints);
    }
}

Линия:

package kpi.ua.shapes;


public class Line {
    private Point from;
    private Point to;
    public Line(Point from, Point to) {
        super();
        this.from = from;
        this.to = to;
    }
    public Point getFrom() {
        return from;
    }
    public void setFrom(Point from) {
        this.from = from;
    }
    public Point getTo() {
        return to;
    }
    public void setTo(Point to) {
        this.to = to;
    }
}

Линия 3D:

package kpi.ua.shapes;

public class Line3D {
    private Point3D from;
    private Point3D to;

    public Line3D() {
        super();
    }

    public Line3D(Point3D from, Point3D to) {
        super();
        this.from = from;
        this.to = to;
    }

    public Point3D getFrom() {
        return from;
    }

    public void setFrom(Point3D from) {
        this.from = from;
    }

    public Point3D getTo() {
        return to;
    }

    public void setTo(Point3D to) {
        this.to = to;
    }

    public Line get2DVision() {
        return new Line(from.get2DVision(), to.get2DVision());
    }
}

Точка:

package kpi.ua.shapes;

public class Point {

    protected Double x;
    protected Double y;

    public Point() {
    }

    public Point(Double x, Double y) {
        this.x = x;
        this.y = y;
    }

    public Double getX() {
        return x;
    }

    public void setX(Double x) {
        this.x = x;
    }

    public Double getY() {
        return y;
    }

    public void setY(Double y) {
        this.y = y;
    }
}

Точка3D:

package kpi.ua.shapes;

public class Point3D extends Point {
    private Double z;

    public Point3D(Double x, Double y, Double z) {
        super(x, y);
        this.z = z;
    }

    public Double getZ() {
        return z;
    }

    public void setZ(Double z) {
        this.z = z;
    }

    /**Èçîìåòðèÿ
     * @return
     */
    public Point get2DVision() {
        Double cx=(1.0/Math.sqrt(6))*(Math.sqrt(3)*this.x-Math.sqrt(3)*this.z);
        Double cy=(1.0/Math.sqrt(6))*(Math.sqrt(2)*this.x-Math.sqrt(2)*this.y+Math.sqrt(2)*this.z);
        return new Point(cx, cy);
    }

    @Override
    public String toString() {
        return "Point3D [z=" + z + ", x=" + x + ", y=" + y + "]";
    }


}

SurfaceBasedOnBuzierCurve:

package kpi.ua.shapes;

import java.awt.Graphics2D;
import java.util.LinkedList;
import java.util.List;

import kpi.ua.util.ControlUtil;
import kpi.ua.util.GraphicUtil;

public class SurfaceBasedOnBuzierCurve {
    private final static Double DEFAULT_STEP_BY_T = 0.05;
    private final static Double DEFAULT_Q = 1.0;
    private final static Double DEFAULT_STEP_BY_FI = 0.15;
    private BezierCurve movingCurve;
    private List<Point3D> buzierPoints;

    public SurfaceBasedOnBuzierCurve() {
        refreshBuzierCurve();
    }

    public List<Point3D> getBuzierPoints() {
        return buzierPoints;
    }

    public void setConePoints(List<Point3D> buzierPoints) {
        this.buzierPoints = buzierPoints;
    }

    public void calcBuzierPoints() {
        this.buzierPoints = new LinkedList<Point3D>();
        Double q = DEFAULT_Q;
        Point movingPoint;
        for(double fi=0.0;fi<Math.PI*2;fi+=DEFAULT_STEP_BY_FI){
            for(double t=0.0;t<=1.0;t+=DEFAULT_STEP_BY_T){
                movingPoint=this.movingCurve.getValue(t);
                Point3D firstPoint =getSurfacePoint(movingPoint, movingPoint, q, fi);
                if(t+DEFAULT_STEP_BY_T<=1){
                    movingPoint=this.movingCurve.getValue(t+DEFAULT_STEP_BY_T);
                    Point3D secondPoint =getSurfacePoint(movingPoint, movingPoint, q, fi);
                    Point3D thirdPoint=getSurfacePoint(movingPoint, movingPoint, q, fi+DEFAULT_STEP_BY_FI);
                    buzierPoints.add(firstPoint);
                    buzierPoints.add(thirdPoint);
                    buzierPoints.add(secondPoint);

                }               
            }
        }
    }

    /**
     * Gets 3DPoint on surface via roating line
     * 
     * @param from
     * @param to
     * @param q
     * @param fi
     * @return
     */
    public Point3D getSurfacePoint(Point from, Point to, Double q, Double fi) {
        Double x = from.getX() + (to.getX() - from.getX()) * q;
        Double y = (from.getY() + (to.getY() - from.getY()) * q) * Math.cos(fi);
        Double z = (from.getY() + (to.getY() - from.getY()) * q) * Math.sin(fi);
        return new Point3D(x, y, z);
    }

    public void drawBuzierSurface(Graphics2D gr) {
        if (buzierPoints == null)
            calcBuzierPoints();
        GraphicUtil.drawTriangulation(gr, buzierPoints);
    }
    public void refreshBuzierCurve(){
        LinkedList<Point> allPoints = new LinkedList<>();
        allPoints.add(new Point(ControlUtil.BUZIER_FIRST_X, ControlUtil.BUZIER_FIRST_Y));
        allPoints.add(new Point(ControlUtil.BUZIER_SECOND_X, ControlUtil.BUZIER_SECOND_Y));
        allPoints.add(new Point(ControlUtil.BUZIER_THIRD_X, ControlUtil.BUZIER_THIRD_Y));
        allPoints.add(new Point(ControlUtil.BUZIER_FOURTH_X, ControlUtil.BUZIER_FOURTH_X));
        allPoints.add(new Point(ControlUtil.BUZIER_FIFTH_X, ControlUtil.BUZIER_FIFTH_X));
        this.movingCurve = new BezierCurve(allPoints);
    }
}

ControlUtil:

package kpi.ua.util;

import kpi.ua.shapes.Point;
import kpi.ua.shapes.Point3D;

public class ControlUtil {
    public static Double scaleX=1.0;
    public static Double scaleY=1.0;
    public static Double scaleZ=1.0;
    public static long serialVersionUID = 45L;
    public static Double AXIS_LENGTH = 200.0;
    public static Point CENTER_AXISES = new Point(300.0, 200.0);
    public static Point3D AXIS_X;
    public static Point3D AXIS_Y;
    public static Point3D AXIS_Z;
    public static Double CONE_PARAMETR_A=4.0;
    public static Double CONE_PARAMETR_B=6.0;
    public static Double CONE_PARAMETR_C=5.0;
    public static Double CONE_START_RADIOUS=25.0;
    public static Double STEP_BY_FI=0.2;
    public static Boolean isDrawCone=false;


//  allPoints.add(new Point(30.0, 10.0));
//  allPoints.add(new Point(300.0, 210.0));
//  allPoints.add(new Point(100.0, 200.0));
//  allPoints.add(new Complex((double) 55.0, (double) 251.0));
//  allPoints.add(new Complex((double) 155.0, (double) 300.0)); 

    public static Double BUZIER_FIRST_X=30.0;
    public static Double BUZIER_FIRST_Y=10.0;

    public static Double BUZIER_SECOND_X=-150.0;
    public static Double BUZIER_SECOND_Y=110.0;

    public static Double BUZIER_THIRD_X=100.0;
    public static Double BUZIER_THIRD_Y=200.0;

    public static Double BUZIER_FOURTH_X=55.0;
    public static Double BUZIER_FOURTH_Y=251.0;

    public static Double BUZIER_FIFTH_X=155.0;
    public static Double BUZIER_FIFTH_Y=300.0;


    static{
        AXIS_X=new Point3D(AXIS_LENGTH, 0.0 ,0.0);
        AXIS_Y=new Point3D(0.0,AXIS_LENGTH, 0.0);
        AXIS_Z=new Point3D(0.0, 0.0, AXIS_LENGTH);
    }
}

Графическое использование:

package kpi.ua.util;

import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.Polygon;
import java.util.Iterator;
import java.util.List;

import kpi.ua.shapes.Line;
import kpi.ua.shapes.Line3D;
import kpi.ua.shapes.Point3D;

public class GraphicUtil{
    public static void drawAxis(Graphics2D gr){
        gr.setColor(Color.RED);
        drawLine(gr,new Line3D(new Point3D(0.0,0.0,0.0),ControlUtil.AXIS_X));
        gr.setColor(Color.WHITE);
        gr.drawString("x", ControlUtil.CENTER_AXISES.getX().intValue()+ControlUtil.AXIS_X.get2DVision().getX().intValue()+5,
                ControlUtil.CENTER_AXISES.getY().intValue()-ControlUtil.AXIS_X.get2DVision().getY().intValue());
        gr.setColor(Color.RED);
        drawLine(gr,new Line3D(new Point3D(0.0,0.0,0.0),ControlUtil.AXIS_Y));
        gr.setColor(Color.WHITE);
        gr.drawString("y", ControlUtil.CENTER_AXISES.getX().intValue()+ControlUtil.AXIS_Y.get2DVision().getX().intValue()+5,
                ControlUtil.CENTER_AXISES.getY().intValue()-ControlUtil.AXIS_Y.get2DVision().getY().intValue());
        gr.setColor(Color.RED);     
        drawLine(gr,new Line3D(new Point3D(0.0,0.0,0.0),ControlUtil.AXIS_Z));
        gr.setColor(Color.WHITE);
        gr.drawString("z", ControlUtil.CENTER_AXISES.getX().intValue()+ControlUtil.AXIS_Z.get2DVision().getX().intValue()+5,
                ControlUtil.CENTER_AXISES.getY().intValue()-ControlUtil.AXIS_Z.get2DVision().getY().intValue());
    }
    public static void moveAroundZ(Double fi,List<Point3D> shape){
        Double x;
        Double y;
        if(shape!=null){
            for(Point3D point:shape){
                x=point.getX()*Math.cos(fi)+point.getY()*Math.sin(fi);
                y=point.getX()*Math.sin(fi)-point.getY()*Math.cos(fi);
                point.setX(x);
                point.setY(y);
            }
        }
    }
    public static void moveAroundX(Double fi,List<Point3D> shape){
        Double y;
        Double z;
        if(shape!=null){
            for(Point3D point:shape){
                y=-point.getY()*Math.cos(fi)-point.getZ()*Math.sin(fi);
                z=-point.getY()*Math.sin(fi)+point.getZ()*Math.cos(fi);
                point.setY(y);
                point.setZ(z);
            }
        }
    }
    public static void moveAroundY(Double fi,List<Point3D> shape){
        Double x;
        Double z;       
        if(shape!=null){
            for(Point3D point:shape){
                x=point.getX()*Math.cos(fi)+point.getZ()*Math.sin(fi);
                z=-point.getX()*Math.sin(fi)+point.getZ()*Math.cos(fi);
                point.setX(x);
                point.setZ(z);
            }
        }
    }
    public static void drawLine(Graphics2D gr,Line3D line3D){
        line3D.getFrom().setX(ControlUtil.scaleX*line3D.getFrom().getX());
        line3D.getFrom().setY(ControlUtil.scaleY*line3D.getFrom().getY());
        line3D.getFrom().setZ(ControlUtil.scaleZ*line3D.getFrom().getZ());
        line3D.getTo().setX(ControlUtil.scaleX*line3D.getTo().getX());
        line3D.getTo().setY(ControlUtil.scaleY*line3D.getTo().getY());
        line3D.getTo().setZ(ControlUtil.scaleZ*line3D.getTo().getZ());
        Line line=line3D.get2DVision();
        gr.drawLine((int)Math.round(ControlUtil.CENTER_AXISES.getX()+line.getFrom().getX()), (int)Math.round(ControlUtil.CENTER_AXISES.getY()-line.getFrom().getY()), (int)Math.round(ControlUtil.CENTER_AXISES.getX()+line.getTo().getX()),(int) Math.round(ControlUtil.CENTER_AXISES.getY()-line.getTo().getY()));
    }

    public static void drawTriangulation(Graphics2D gr,List<Point3D> shape){
        if(shape==null)
            return;
        Iterator<Point3D> iter=shape.iterator();
        Point3D from=null;
        Point3D to=null;
        while(iter.hasNext()){
            from=iter.next();
            if(iter.hasNext()){
                to=iter.next();
                GraphicUtil.drawLine(gr, new Line3D(from,to));
                if(iter.hasNext()){
                    from=iter.next();
                    GraphicUtil.drawLine(gr, new Line3D(from,to));                  
                }
            }
        }       
    }
}
person Maksym    schedule 01.12.2014