Reduce how often arrays need to be initialised and create interface for blender plugin

pull/54/head
James Ball 2022-04-24 22:10:10 +01:00
rodzic 49f4fcfc5f
commit 5b4c94a5bc
4 zmienionych plików z 199 dodań i 88 usunięć

Wyświetl plik

@ -14,57 +14,123 @@ col["seen_objs"] = {}
camera = bpy.context.scene.camera
def my_osci_render_func(scene):
engine_info = {"objects": []}
new_objs = []
for obj in bpy.data.objects:
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]
camera_space = camera.matrix_world.inverted() @ obj.matrix_world
object_info["matrix"] = [camera_space[i][j] for i in range(4) for j in range(4)]
print(camera_space)
engine_info["objects"].append(object_info)
sock = None
engine_info["focalLength"] = -0.1 * bpy.data.cameras[0].lens
class OBJECT_PT_osci_render_settings(bpy.types.Panel):
bl_idname = "OBJECT_PT_osci_render_settings"
bl_label = "osci-render settings"
bl_space_type = "PROPERTIES"
bl_region_type = "WINDOW"
bl_context = "render"
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
try:
sock.connect((HOST, PORT))
sock.sendall(json.dumps(engine_info, separators=(',', ':')).encode('utf-8'))
sock.close()
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)
def draw_header(self, context):
layout = self.layout
def draw(self, context):
global sock
if sock is None:
self.layout.operator("render.osci_render_connect", text="Connect to osci-render")
else:
self.layout.operator("render.osci_render_close", text="Close osci-render connection")
class osci_render_connect(bpy.types.Operator):
bl_label = "Connect to osci-render"
bl_idname = "render.osci_render_connect"
bl_description = "Connect to osci-render"
bpy.app.handlers.frame_change_pre.clear()
bpy.app.handlers.frame_change_pre.append(my_osci_render_func)
def execute(self, context):
global sock
if sock is None:
try:
col["seen_objs"] = {}
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect((HOST, PORT))
send_scene_to_osci_render(bpy.context.scene)
except OSError as exp:
sock.close()
sock = None
bpy.app.handlers.depsgraph_update_post.clear()
bpy.app.handlers.depsgraph_update_post.append(my_osci_render_func)
return {"FINISHED"}
class osci_render_close(bpy.types.Operator):
bl_label = "Close osci-render connection"
bl_idname="render.osci_render_close"
def execute(self, context):
global sock
if sock is not None:
sock.send("CLOSE\n".encode('utf-8'))
sock.close()
sock = None
return {"FINISHED"}
def send_scene_to_osci_render(scene):
global sock
if sock is not None:
engine_info = {"objects": []}
new_objs = []
for obj in bpy.data.objects:
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]
camera_space = camera.matrix_world.inverted() @ obj.matrix_world
object_info["matrix"] = [camera_space[i][j] for i in range(4) for j in range(4)]
engine_info["objects"].append(object_info)
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)
operations = [OBJECT_PT_osci_render_settings, osci_render_connect, osci_render_close]
def register():
bpy.app.handlers.frame_change_pre.append(send_scene_to_osci_render)
bpy.app.handlers.depsgraph_update_post.append(send_scene_to_osci_render)
for operation in operations:
bpy.utils.register_class(operation)
def unregister():
bpy.app.handlers.frame_change_pre.clear()
bpy.app.handlers.depsgraph_update_post.clear()
for operation in operations.reverse():
bpy.utils.unregister_class(operation)
if __name__ == "__main__":
register()

Wyświetl plik

@ -9,12 +9,12 @@ import sh.ball.shapes.Vector2;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
public class CameraDrawKernel extends Kernel {
private WorldObject prevObject = null;
private List<WorldObject> prevObjects = null;
private float[] vertices;
private float[] vertexResult;
private float[] triangles;
@ -57,33 +57,38 @@ public class CameraDrawKernel extends Kernel {
public List<Shape> draw(ObjectSet objects, float focalLength) {
this.focalLength = focalLength;
usingObjectSet = 1;
List<List<List<Vector3>>> vertices = objects.objects.stream().map(WorldObject::getVertexPath).toList();
this.vertexNums = vertices.stream().mapToInt(
l -> l.stream()
.map(List::size)
.reduce(0, Integer::sum) + l.size()
).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() * 16];
List<WorldObject> objectList = objects.objects.stream().toList();
if (!objectList.equals(prevObjects)) {
prevObjects = objectList;
List<List<List<Vector3>>> vertices = objectList.stream().map(WorldObject::getVertexPath).toList();
this.vertexNums = vertices.stream().mapToInt(
l -> l.stream()
.map(List::size)
.reduce(0, Integer::sum) + l.size()
).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() * 16];
List<float[]> triangles = objectList.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);
}
}
int offset = 0;
for (float[] matrix : objects.cameraSpaceMatrices) {
System.arraycopy(matrix, 0, this.matrices, offset, matrix.length);
offset += matrix.length;
}
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];
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);
}
this.cameraPosX = 0;
this.cameraPosY = 0;

Wyświetl plik

@ -16,6 +16,13 @@ public class ObjectServer implements Runnable {
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;
public ObjectServer(Runnable enableRendering, Runnable disableRendering) {
this.enableRendering = enableRendering;
this.disableRendering = disableRendering;
}
@Override
public void run() {
@ -24,21 +31,28 @@ public class ObjectServer implements Runnable {
while (true) {
Socket socket = Server.accept();
enableRendering.run();
BufferedReader clientReader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
String json = clientReader.readLine();
EngineInfo info = gson.fromJson(json, EngineInfo.class);
for (ObjectInfo obj : info.objects) {
if (!objects.containsKey(obj.name)) {
objects.put(obj.name, new WorldObject(obj.vertices, obj.edges, obj.faces));
while (socket.isConnected()) {
String json = clientReader.readLine();
if (json.equals("CLOSE")) {
socket.close();
break;
}
EngineInfo info = gson.fromJson(json, EngineInfo.class);
for (ObjectInfo obj : info.objects) {
if (!objects.containsKey(obj.name)) {
objects.put(obj.name, new WorldObject(obj.vertices, obj.edges, obj.faces));
}
}
Set<String> currentObjects = Arrays.stream(info.objects).map(obj -> obj.name).collect(Collectors.toSet());
objects.entrySet().removeIf(obj -> !currentObjects.contains(obj.getKey()));
objectSet.setObjects(objects.values(), Arrays.stream(info.objects).map(obj -> obj.matrix).toList(), info.focalLength);
}
Set<String> currentObjects = Arrays.stream(info.objects).map(obj -> obj.name).collect(Collectors.toSet());
objects.entrySet().removeIf(obj -> !currentObjects.contains(obj.getKey()));
socket.close();
objectSet.setObjects(objects.values(), Arrays.stream(info.objects).map(obj -> obj.matrix).toList(), info.focalLength);
disableRendering.run();
}
} catch (IOException e) {
e.printStackTrace();

Wyświetl plik

@ -49,6 +49,7 @@ import sh.ball.audio.engine.JavaAudioInput;
import sh.ball.audio.midi.MidiListener;
import sh.ball.audio.midi.MidiNote;
import sh.ball.engine.ObjectServer;
import sh.ball.engine.ObjectSet;
import sh.ball.gui.Gui;
import sh.ball.parser.obj.ObjFrameSettings;
import sh.ball.parser.obj.ObjParser;
@ -98,6 +99,7 @@ public class MainController implements Initializable, FrequencyListener, MidiLis
private List<FrameSource<List<Shape>>> frameSources = new ArrayList<>();
private FrameProducer<List<Shape>> producer;
private int currentFrameSource;
private boolean objectServerRendering = false;
// javafx
private final FileChooser osciFileChooser = new FileChooser();
@ -454,13 +456,37 @@ public class MainController implements Initializable, FrequencyListener, MidiLis
}
}
server = new ObjectServer();
openFiles.add(null);
frameSources.add(server.getObjectSet());
frameSourcePaths.add("BLENDER");
server = new ObjectServer(this::enableObjectServerRendering, this::disableObjectServerRendering);
new Thread(server).start();
}
private void enableObjectServerRendering() {
Platform.runLater(() -> {
objectServerRendering = true;
ObjectSet set = server.getObjectSet();
frameSources.forEach(FrameSource::disable);
set.enable();
producer = new FrameProducer<>(audioPlayer, set);
objController.setAudioProducer(producer);
executor.submit(producer);
effectsController.restartEffects();
generalController.setFrameSourceName("Rendering from external input");
generalController.updateFrameLabels();
objTitledPane.setDisable(true);
});
}
private void disableObjectServerRendering() {
Platform.runLater(() -> {
server.getObjectSet().disable();
objectServerRendering = false;
changeFrameSource(currentFrameSource);
});
}
// used when a file is chosen so that the same folder is reopened when a
// file chooser opens
private void updateLastVisitedDirectory(File file) {
@ -577,7 +603,7 @@ public class MainController implements Initializable, FrequencyListener, MidiLis
// increments and changes the frameSource after pressing 'j'
public void nextFrameSource() {
if (frameSources.size() == 1) {
if (objectServerRendering || frameSources.size() == 1) {
return;
}
int index = currentFrameSource + 1;
@ -589,7 +615,7 @@ public class MainController implements Initializable, FrequencyListener, MidiLis
// decrements and changes the frameSource after pressing 'k'
public void previousFrameSource() {
if (frameSources.size() == 1) {
if (objectServerRendering || frameSources.size() == 1) {
return;
}
int index = currentFrameSource - 1;