kopia lustrzana https://github.com/jameshball/osci-render
Reduce how often arrays need to be initialised and create interface for blender plugin
rodzic
49f4fcfc5f
commit
5b4c94a5bc
|
@ -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()
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
|
|
Ładowanie…
Reference in New Issue