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
|
camera = bpy.context.scene.camera
|
||||||
|
|
||||||
|
sock = None
|
||||||
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)
|
|
||||||
|
|
||||||
|
|
||||||
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)
|
def draw_header(self, context):
|
||||||
try:
|
layout = self.layout
|
||||||
sock.connect((HOST, PORT))
|
|
||||||
sock.sendall(json.dumps(engine_info, separators=(',', ':')).encode('utf-8'))
|
def draw(self, context):
|
||||||
sock.close()
|
global sock
|
||||||
except OSError as exc:
|
if sock is None:
|
||||||
# Remove all newly added objects if no connection was made
|
self.layout.operator("render.osci_render_connect", text="Connect to osci-render")
|
||||||
# so that the object data will be sent on next attempt
|
else:
|
||||||
for obj_name in new_objs:
|
self.layout.operator("render.osci_render_close", text="Close osci-render connection")
|
||||||
col["seen_objs"].pop(obj_name)
|
|
||||||
|
|
||||||
|
|
||||||
|
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()
|
def execute(self, context):
|
||||||
bpy.app.handlers.frame_change_pre.append(my_osci_render_func)
|
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()
|
return {"FINISHED"}
|
||||||
bpy.app.handlers.depsgraph_update_post.append(my_osci_render_func)
|
|
||||||
|
|
||||||
|
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.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public class CameraDrawKernel extends Kernel {
|
public class CameraDrawKernel extends Kernel {
|
||||||
|
|
||||||
private WorldObject prevObject = null;
|
private WorldObject prevObject = null;
|
||||||
|
private List<WorldObject> prevObjects = null;
|
||||||
private float[] vertices;
|
private float[] vertices;
|
||||||
private float[] vertexResult;
|
private float[] vertexResult;
|
||||||
private float[] triangles;
|
private float[] triangles;
|
||||||
|
@ -57,33 +57,38 @@ public class CameraDrawKernel extends Kernel {
|
||||||
public List<Shape> draw(ObjectSet objects, float focalLength) {
|
public List<Shape> draw(ObjectSet objects, float focalLength) {
|
||||||
this.focalLength = focalLength;
|
this.focalLength = focalLength;
|
||||||
usingObjectSet = 1;
|
usingObjectSet = 1;
|
||||||
List<List<List<Vector3>>> vertices = objects.objects.stream().map(WorldObject::getVertexPath).toList();
|
List<WorldObject> objectList = objects.objects.stream().toList();
|
||||||
this.vertexNums = vertices.stream().mapToInt(
|
if (!objectList.equals(prevObjects)) {
|
||||||
l -> l.stream()
|
prevObjects = objectList;
|
||||||
.map(List::size)
|
List<List<List<Vector3>>> vertices = objectList.stream().map(WorldObject::getVertexPath).toList();
|
||||||
.reduce(0, Integer::sum) + l.size()
|
this.vertexNums = vertices.stream().mapToInt(
|
||||||
).toArray();
|
l -> l.stream()
|
||||||
int numVertices = Arrays.stream(vertexNums).sum();
|
.map(List::size)
|
||||||
this.vertices = new float[numVertices * 3];
|
.reduce(0, Integer::sum) + l.size()
|
||||||
this.vertexResult = new float[numVertices * 2];
|
).toArray();
|
||||||
this.matrices = new float[vertices.size() * 16];
|
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;
|
int offset = 0;
|
||||||
for (float[] matrix : objects.cameraSpaceMatrices) {
|
for (float[] matrix : objects.cameraSpaceMatrices) {
|
||||||
System.arraycopy(matrix, 0, this.matrices, offset, matrix.length);
|
System.arraycopy(matrix, 0, this.matrices, offset, matrix.length);
|
||||||
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.cameraPosX = 0;
|
||||||
this.cameraPosY = 0;
|
this.cameraPosY = 0;
|
||||||
|
|
|
@ -16,6 +16,13 @@ public class ObjectServer implements Runnable {
|
||||||
private final Gson gson = new Gson();
|
private final Gson gson = new Gson();
|
||||||
private final Map<String, WorldObject> objects = new HashMap<>();
|
private final Map<String, WorldObject> objects = new HashMap<>();
|
||||||
private final ObjectSet objectSet = new ObjectSet();
|
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
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
|
@ -24,21 +31,28 @@ public class ObjectServer implements Runnable {
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
Socket socket = Server.accept();
|
Socket socket = Server.accept();
|
||||||
|
enableRendering.run();
|
||||||
BufferedReader clientReader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
|
BufferedReader clientReader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
|
||||||
String json = clientReader.readLine();
|
while (socket.isConnected()) {
|
||||||
EngineInfo info = gson.fromJson(json, EngineInfo.class);
|
String json = clientReader.readLine();
|
||||||
|
if (json.equals("CLOSE")) {
|
||||||
for (ObjectInfo obj : info.objects) {
|
socket.close();
|
||||||
if (!objects.containsKey(obj.name)) {
|
break;
|
||||||
objects.put(obj.name, new WorldObject(obj.vertices, obj.edges, obj.faces));
|
|
||||||
}
|
}
|
||||||
|
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);
|
||||||
}
|
}
|
||||||
|
disableRendering.run();
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
|
|
|
@ -49,6 +49,7 @@ import sh.ball.audio.engine.JavaAudioInput;
|
||||||
import sh.ball.audio.midi.MidiListener;
|
import sh.ball.audio.midi.MidiListener;
|
||||||
import sh.ball.audio.midi.MidiNote;
|
import sh.ball.audio.midi.MidiNote;
|
||||||
import sh.ball.engine.ObjectServer;
|
import sh.ball.engine.ObjectServer;
|
||||||
|
import sh.ball.engine.ObjectSet;
|
||||||
import sh.ball.gui.Gui;
|
import sh.ball.gui.Gui;
|
||||||
import sh.ball.parser.obj.ObjFrameSettings;
|
import sh.ball.parser.obj.ObjFrameSettings;
|
||||||
import sh.ball.parser.obj.ObjParser;
|
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 List<FrameSource<List<Shape>>> frameSources = new ArrayList<>();
|
||||||
private FrameProducer<List<Shape>> producer;
|
private FrameProducer<List<Shape>> producer;
|
||||||
private int currentFrameSource;
|
private int currentFrameSource;
|
||||||
|
private boolean objectServerRendering = false;
|
||||||
|
|
||||||
// javafx
|
// javafx
|
||||||
private final FileChooser osciFileChooser = new FileChooser();
|
private final FileChooser osciFileChooser = new FileChooser();
|
||||||
|
@ -454,13 +456,37 @@ public class MainController implements Initializable, FrequencyListener, MidiLis
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
server = new ObjectServer();
|
server = new ObjectServer(this::enableObjectServerRendering, this::disableObjectServerRendering);
|
||||||
openFiles.add(null);
|
|
||||||
frameSources.add(server.getObjectSet());
|
|
||||||
frameSourcePaths.add("BLENDER");
|
|
||||||
new Thread(server).start();
|
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
|
// used when a file is chosen so that the same folder is reopened when a
|
||||||
// file chooser opens
|
// file chooser opens
|
||||||
private void updateLastVisitedDirectory(File file) {
|
private void updateLastVisitedDirectory(File file) {
|
||||||
|
@ -577,7 +603,7 @@ public class MainController implements Initializable, FrequencyListener, MidiLis
|
||||||
|
|
||||||
// increments and changes the frameSource after pressing 'j'
|
// increments and changes the frameSource after pressing 'j'
|
||||||
public void nextFrameSource() {
|
public void nextFrameSource() {
|
||||||
if (frameSources.size() == 1) {
|
if (objectServerRendering || frameSources.size() == 1) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
int index = currentFrameSource + 1;
|
int index = currentFrameSource + 1;
|
||||||
|
@ -589,7 +615,7 @@ public class MainController implements Initializable, FrequencyListener, MidiLis
|
||||||
|
|
||||||
// decrements and changes the frameSource after pressing 'k'
|
// decrements and changes the frameSource after pressing 'k'
|
||||||
public void previousFrameSource() {
|
public void previousFrameSource() {
|
||||||
if (frameSources.size() == 1) {
|
if (objectServerRendering || frameSources.size() == 1) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
int index = currentFrameSource - 1;
|
int index = currentFrameSource - 1;
|
||||||
|
|
Ładowanie…
Reference in New Issue