kopia lustrzana https://github.com/jameshball/osci-render
Remove 3D object rendering from blender and only keep grease line rendering
rodzic
d0746cebcf
commit
934954f4d5
|
@ -4,8 +4,8 @@ bl_info = {
|
|||
"version": (1, 0, 0),
|
||||
"blender": (3, 1, 2),
|
||||
"location": "View3D",
|
||||
"description": "Addon to send frames over to osci-render",
|
||||
"warning": "Requires a camera and objects",
|
||||
"description": "Addon to send gpencil frames over to osci-render",
|
||||
"warning": "Requires a camera and gpencil object",
|
||||
"wiki_url": "https://github.com/jameshball/osci-render",
|
||||
"category": "Development",
|
||||
}
|
||||
|
@ -48,8 +48,6 @@ class osci_render_connect(bpy.types.Operator):
|
|||
global sock
|
||||
if sock is None:
|
||||
try:
|
||||
bpy.context.scene.collection["osci_render"] = {}
|
||||
bpy.context.scene.collection["osci_render"]["seen_objs"] = {}
|
||||
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
sock.connect((HOST, PORT))
|
||||
send_scene_to_osci_render(bpy.context.scene)
|
||||
|
@ -62,7 +60,7 @@ class osci_render_connect(bpy.types.Operator):
|
|||
|
||||
class osci_render_close(bpy.types.Operator):
|
||||
bl_label = "Close osci-render connection"
|
||||
bl_idname="render.osci_render_close"
|
||||
bl_idname = "render.osci_render_close"
|
||||
|
||||
def execute(self, context):
|
||||
global sock
|
||||
|
@ -86,103 +84,29 @@ def append_matrix(object_info, obj):
|
|||
|
||||
def send_scene_to_osci_render(scene):
|
||||
col = bpy.context.scene.collection["osci_render"]
|
||||
engine_info = {"objects": []}
|
||||
|
||||
if sock is not None:
|
||||
engine_info = {"objects": []}
|
||||
new_objs = []
|
||||
|
||||
for obj in bpy.data.objects:
|
||||
if obj.visible_get():
|
||||
# if obj.type == 'MESH':
|
||||
# object_info = {"name": obj.name}
|
||||
# if obj.name not in col["seen_objs"]:
|
||||
# col["seen_objs"][obj.name] = 1
|
||||
# new_objs.append(obj.name)
|
||||
|
||||
# mesh = bmesh.new()
|
||||
# mesh.from_mesh(obj.data)
|
||||
|
||||
# object_info["vertices"] = []
|
||||
# # If there are bugs, the vertices here might not match up with the vert.index in edges/faces
|
||||
# for vert in mesh.verts:
|
||||
# object_info["vertices"].append({
|
||||
# "x": vert.co[0],
|
||||
# "y": vert.co[1],
|
||||
# "z": vert.co[2],
|
||||
# })
|
||||
|
||||
# object_info["edges"] = [vert.index for edge in mesh.edges for vert in edge.verts]
|
||||
# object_info["faces"] = [[vert.index for vert in face.verts] for face in mesh.faces]
|
||||
|
||||
# engine_info["objects"].append(append_matrix(object_info, obj))
|
||||
if obj.type == 'GPENCIL':
|
||||
object_info = {"name": obj.name}
|
||||
strokes = obj.data.layers.active.frames.data.active_frame.strokes
|
||||
|
||||
print("found gpencil!")
|
||||
print(strokes)
|
||||
|
||||
|
||||
object_info["pathVertices"] = []
|
||||
for stroke in strokes:
|
||||
for vert in stroke.points:
|
||||
object_info["pathVertices"].append({
|
||||
"x": vert.co[0],
|
||||
"y": vert.co[1],
|
||||
"z": vert.co[2],
|
||||
})
|
||||
# end of path
|
||||
object_info["pathVertices"].append({
|
||||
"x": float("nan"),
|
||||
"y": float("nan"),
|
||||
"z": float("nan"),
|
||||
})
|
||||
|
||||
engine_info["objects"].append(append_matrix(object_info, obj))
|
||||
# elif obj.type == 'CURVE':
|
||||
# object_info = {"name": obj.name}
|
||||
# for curve in obj.data.splines:
|
||||
# if curve.type == 'BEZIER':
|
||||
# object_info["bezierPoints"] = []
|
||||
# points = list(curve.bezier_points)
|
||||
# if is_cyclic(curve) and len(points) > 0:
|
||||
# points.append(points[0])
|
||||
#
|
||||
# for point in points:
|
||||
# for co in [point.co, point.handle_left, point.handle_right]:
|
||||
# object_info["bezierPoints"].append({
|
||||
# "x": co[0],
|
||||
# "y": co[1],
|
||||
# "z": co[2],
|
||||
# })
|
||||
#
|
||||
# engine_info["objects"].append(append_matrix(object_info, obj))
|
||||
# elif curve.type == 'POLY':
|
||||
# object_info["polyPoints"] = []
|
||||
# points = list(curve.points)
|
||||
# if is_cyclic(curve) and len(points) > 0:
|
||||
# points.append(points[0])
|
||||
#
|
||||
# object_info["polyPoints"] = [{
|
||||
# "x": point.co[0],
|
||||
# "y": point.co[1],
|
||||
# "z": point.co[2],
|
||||
# } for point in points]
|
||||
#
|
||||
# engine_info["objects"].append(append_matrix(object_info, obj))
|
||||
if obj.visible_get() and obj.type == 'GPENCIL':
|
||||
object_info = {"name": obj.name}
|
||||
strokes = obj.data.layers.active.frames.data.active_frame.strokes
|
||||
|
||||
object_info["vertices"] = []
|
||||
for stroke in strokes:
|
||||
object_info["vertices"].append([{
|
||||
"x": vert.co[0],
|
||||
"y": vert.co[1],
|
||||
"z": vert.co[2],
|
||||
} for vert in stroke.points])
|
||||
|
||||
engine_info["objects"].append(append_matrix(object_info, obj))
|
||||
|
||||
|
||||
engine_info["focalLength"] = -0.05 * bpy.data.cameras[0].lens
|
||||
|
||||
engine_info["focalLength"] = -0.1 * bpy.data.cameras[0].lens
|
||||
|
||||
try:
|
||||
json_str = json.dumps(engine_info, separators=(',', ':')) + '\n'
|
||||
sock.sendall(json_str.encode('utf-8'))
|
||||
except OSError as exc:
|
||||
# Remove all newly added objects if no connection was made
|
||||
# so that the object data will be sent on next attempt
|
||||
for obj_name in new_objs:
|
||||
col["seen_objs"].pop(obj_name)
|
||||
json_str = json.dumps(engine_info, separators=(',', ':')) + '\n'
|
||||
sock.sendall(json_str.encode('utf-8'))
|
||||
|
||||
|
||||
operations = [OBJECT_PT_osci_render_settings, osci_render_connect, osci_render_close]
|
||||
|
|
|
@ -10,13 +10,10 @@ import sh.ball.shapes.Vector2;
|
|||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
public class CameraDrawKernel extends Kernel {
|
||||
|
||||
private WorldObject prevObject = null;
|
||||
private List<WorldObject> prevObjects = null;
|
||||
private List<Vector3[]> prevPaths = null;
|
||||
private float[] vertices;
|
||||
private float[] vertexResult;
|
||||
private float[] triangles = new float[1];
|
||||
|
@ -56,57 +53,40 @@ public class CameraDrawKernel extends Kernel {
|
|||
return count;
|
||||
}
|
||||
|
||||
private int initialiseVertices(int count, Vector3[][] vertices) {
|
||||
for (Vector3[] vectors : vertices) {
|
||||
for (Vector3 vertex : vectors) {
|
||||
this.vertices[3 * count] = (float) vertex.x;
|
||||
this.vertices[3 * count + 1] = (float) vertex.y;
|
||||
this.vertices[3 * count + 2] = (float) vertex.z;
|
||||
count++;
|
||||
}
|
||||
// Set it to NaN so that the line connecting the vertex before and after
|
||||
// this path segment is not drawn.
|
||||
this.vertices[3 * count] = Float.NaN;
|
||||
this.vertices[3 * count + 1] = Float.NaN;
|
||||
this.vertices[3 * count + 2] = Float.NaN;
|
||||
count++;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
public synchronized List<Shape> draw(ObjectSet objects, float focalLength) {
|
||||
this.focalLength = focalLength;
|
||||
usingObjectSet = 1;
|
||||
if (!objects.objects.equals(prevObjects) || !objects.pathObjects.equals(prevPaths)) {
|
||||
prevObjects = objects.objects;
|
||||
prevPaths = objects.pathObjects;
|
||||
List<List<List<Vector3>>> vertices = objects.objects.stream().map(WorldObject::getVertexPath).toList();
|
||||
this.vertexNums = IntStream.concat(
|
||||
vertices.stream().mapToInt(
|
||||
l -> l.stream()
|
||||
.map(List::size)
|
||||
.reduce(0, Integer::sum) + l.size()
|
||||
),
|
||||
objects.pathObjects.stream().mapToInt(
|
||||
arr -> arr.length
|
||||
)
|
||||
).toArray();
|
||||
int numVertices = Arrays.stream(vertexNums).sum();
|
||||
this.vertices = new float[numVertices * 3];
|
||||
this.vertexResult = new float[numVertices * 2];
|
||||
this.matrices = new float[(vertices.size() + objects.pathObjects.size()) * 16];
|
||||
if (!objects.objects.isEmpty()) {
|
||||
List<float[]> triangles = objects.objects.stream().map(WorldObject::getTriangles).toList();
|
||||
int numTriangles = triangles.stream().map(arr -> arr.length).reduce(0, Integer::sum);
|
||||
this.triangles = new float[numTriangles];
|
||||
int offset = 0;
|
||||
for (float[] triangleArray : triangles) {
|
||||
System.arraycopy(triangleArray, 0, this.triangles, offset, triangleArray.length);
|
||||
offset += triangleArray.length;
|
||||
}
|
||||
}
|
||||
int count = 0;
|
||||
for (List<List<Vector3>> vertexList : vertices) {
|
||||
count = initialiseVertices(count, vertexList);
|
||||
}
|
||||
for (Vector3[] vectors : objects.pathObjects) {
|
||||
for (Vector3 vertex : vectors) {
|
||||
this.vertices[3 * count] = (float) vertex.x;
|
||||
this.vertices[3 * count + 1] = (float) vertex.y;
|
||||
this.vertices[3 * count + 2] = (float) vertex.z;
|
||||
count++;
|
||||
}
|
||||
}
|
||||
this.vertexNums = objects.paths.stream().mapToInt(arr -> Arrays.stream(arr).mapToInt(arr2 -> arr2.length + 1).sum()).toArray();
|
||||
int numVertices = Arrays.stream(vertexNums).sum();
|
||||
this.vertices = new float[numVertices * 3];
|
||||
this.vertexResult = new float[numVertices * 2];
|
||||
this.matrices = new float[objects.paths.size() * 16];
|
||||
int count = 0;
|
||||
for (Vector3[][] path : objects.paths) {
|
||||
count = initialiseVertices(count, path);
|
||||
}
|
||||
|
||||
int offset = 0;
|
||||
for (float[] matrix : objects.objectMatrices) {
|
||||
System.arraycopy(matrix, 0, this.matrices, offset, matrix.length);
|
||||
offset += matrix.length;
|
||||
}
|
||||
for (float[] matrix : objects.pathMatrices) {
|
||||
for (float[] matrix : objects.matrices) {
|
||||
System.arraycopy(matrix, 0, this.matrices, offset, matrix.length);
|
||||
offset += matrix.length;
|
||||
}
|
||||
|
|
|
@ -13,7 +13,6 @@ public class ObjectServer implements Runnable {
|
|||
|
||||
private static final int PORT = 51677;
|
||||
private final Gson gson = new Gson();
|
||||
private final Map<String, WorldObject> objects = new HashMap<>();
|
||||
private final ObjectSet objectSet = new ObjectSet();
|
||||
private final Runnable enableRendering;
|
||||
private final Runnable disableRendering;
|
||||
|
@ -40,33 +39,48 @@ public class ObjectServer implements Runnable {
|
|||
}
|
||||
EngineInfo info = gson.fromJson(json, EngineInfo.class);
|
||||
|
||||
List<WorldObject> objectsToRender = new ArrayList<>();
|
||||
List<float[]> objectMatrices = new ArrayList<>();
|
||||
List<Map.Entry<Vector3[][], float[]>> orderedVertices = Arrays.stream(info.objects)
|
||||
.parallel()
|
||||
.filter(obj -> obj.vertices.length > 0)
|
||||
.map(obj -> {
|
||||
boolean[] visited = new boolean[obj.vertices.length];
|
||||
int[] order = new int[obj.vertices.length];
|
||||
visited[0] = true;
|
||||
order[0] = 0;
|
||||
Vector3 endPoint = obj.vertices[0][obj.vertices[0].length - 1];
|
||||
|
||||
List<Vector3[]> pathObjects = new ArrayList<>();
|
||||
List<float[]> pathMatrices = new ArrayList<>();
|
||||
|
||||
Set<String> currentObjects = new HashSet<>();
|
||||
|
||||
for (ObjectInfo obj : info.objects) {
|
||||
currentObjects.add(obj.name);
|
||||
if (!objects.containsKey(obj.name)) {
|
||||
if (obj.vertices != null) {
|
||||
objects.put(obj.name, new WorldObject(obj.vertices, obj.edges, obj.faces));
|
||||
for (int i = 1; i < obj.vertices.length; i++) {
|
||||
int minPath = -1;
|
||||
double minDistance = Double.POSITIVE_INFINITY;
|
||||
for (int j = 0; j < obj.vertices.length; j++) {
|
||||
if (!visited[j]) {
|
||||
double distance = endPoint.distance(obj.vertices[j][0]);
|
||||
if (distance < minDistance) {
|
||||
minPath = j;
|
||||
minDistance = distance;
|
||||
}
|
||||
}
|
||||
}
|
||||
visited[minPath] = true;
|
||||
order[i] = minPath;
|
||||
endPoint = obj.vertices[minPath][obj.vertices[minPath].length - 1];
|
||||
}
|
||||
}
|
||||
if (obj.pathVertices == null) {
|
||||
objectsToRender.add(objects.get(obj.name));
|
||||
objectMatrices.add(obj.matrix);
|
||||
} else {
|
||||
pathObjects.add(obj.pathVertices);
|
||||
pathMatrices.add(obj.matrix);
|
||||
}
|
||||
}
|
||||
|
||||
objects.entrySet().removeIf(obj -> !currentObjects.contains(obj.getKey()));
|
||||
Vector3[][] reorderedVertices = new Vector3[obj.vertices.length][];
|
||||
for (int i = 0; i < reorderedVertices.length; i++) {
|
||||
reorderedVertices[i] = obj.vertices[order[i]];
|
||||
}
|
||||
|
||||
objectSet.setObjects(objectsToRender, objectMatrices, pathObjects, pathMatrices, info.focalLength);
|
||||
return Map.entry(reorderedVertices, obj.matrix);
|
||||
}).toList();
|
||||
|
||||
List<Vector3[][]> vertices = orderedVertices.stream().map(Map.Entry::getKey).toList();
|
||||
List<float[]> matrices = orderedVertices.stream().map(Map.Entry::getValue).toList();
|
||||
|
||||
// List<Vector3[][]> vertices = Arrays.stream(info.objects).map(obj -> obj.vertices).toList();
|
||||
// List<float[]> matrices = Arrays.stream(info.objects).map(obj -> obj.matrix).toList();
|
||||
|
||||
objectSet.setObjects(vertices, matrices, info.focalLength);
|
||||
}
|
||||
disableRendering.run();
|
||||
}
|
||||
|
@ -94,22 +108,8 @@ public class ObjectServer implements Runnable {
|
|||
|
||||
private static class ObjectInfo {
|
||||
private String name;
|
||||
private Vector3[] vertices;
|
||||
private Vector3[] pathVertices;
|
||||
private int[] edges;
|
||||
private int[][] faces;
|
||||
private Vector3[][] vertices;
|
||||
// Camera space matrix
|
||||
private float[] matrix;
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "ObjectInfo{" +
|
||||
"name='" + name + '\'' +
|
||||
", vertices=" + Arrays.toString(vertices) +
|
||||
", edges=" + Arrays.toString(edges) +
|
||||
", faces=" + Arrays.deepToString(faces) +
|
||||
", matrix=" + Arrays.toString(matrix) +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,53 +5,27 @@ import sh.ball.shapes.Shape;
|
|||
import sh.ball.shapes.Vector2;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
public class ObjectSet implements FrameSource<List<Shape>> {
|
||||
|
||||
private final CameraDrawKernel kernel = new CameraDrawKernel();
|
||||
|
||||
public List<WorldObject> objects;
|
||||
public List<float[]> objectMatrices;
|
||||
public List<Vector3[]> pathObjects;
|
||||
public List<float[]> pathMatrices;
|
||||
public List<Vector3[][]> paths;
|
||||
public List<float[]> matrices;
|
||||
private boolean active = true;
|
||||
private float focalLength;
|
||||
|
||||
public ObjectSet() {}
|
||||
|
||||
public synchronized void setObjects(List<WorldObject> objects, List<float[]> matrices, List<Vector3[]> pathObjects, List<float[]> pathMatrices, float focalLength) {
|
||||
this.objects = objects;
|
||||
this.objectMatrices = matrices;
|
||||
this.pathObjects = pathObjects;
|
||||
this.pathMatrices = pathMatrices;
|
||||
public synchronized void setObjects(List<Vector3[][]> paths, List<float[]> matrices, float focalLength) {
|
||||
this.paths = paths;
|
||||
this.matrices = matrices;
|
||||
this.focalLength = focalLength;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
|
||||
ObjectSet objectSet = (ObjectSet) o;
|
||||
|
||||
if (!Objects.equals(objects, objectSet.objects))
|
||||
return false;
|
||||
return Objects.equals(objectMatrices, objectSet.objectMatrices);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = objects != null ? objects.hashCode() : 0;
|
||||
result = 31 * result + (objectMatrices != null ? objectMatrices.hashCode() : 0);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized List<Shape> next() {
|
||||
if ((objects == null || objectMatrices == null || objects.isEmpty() || objectMatrices.isEmpty())
|
||||
&& (pathObjects == null || pathMatrices == null || pathObjects.isEmpty() || pathMatrices.isEmpty())) {
|
||||
System.out.println("nothing to draw!");
|
||||
if (paths == null || matrices == null || paths.isEmpty() || matrices.isEmpty()) {
|
||||
return List.of(new Vector2());
|
||||
}
|
||||
return kernel.draw(this, focalLength);
|
||||
|
|
Ładowanie…
Reference in New Issue