nskrypnik / kivy3

3D graphics framework for Kivy.
MIT License
85 stars 42 forks source link

Add new objects to Renderer after Renderer.render() called #22

Open perceptualJonathan opened 7 years ago

perceptualJonathan commented 7 years ago

It does not appear possible to add extra objects to a scene after the initial Renderer.render() call. This is seen in a simple modification of example3.py. Here, when myFlag=1, all 4 of the box, sphere, cylinder, and pyramid should be visible, but only the first 2 are visible. The other 2 are in self.scene.children, but do not appear on screen. When myFlag=0, the box is initially the only object in the scene, then the sphere is to appear when box.pos.z = -20, and this repeats with the cylinder and pyramid. However, none of the sphere, cylinder, or pyramid become visible. Again, self.scene.children contains all 4 objects, but only the box is visible. It's not clear to me why it would be required that all objects be created at the beginning, as opposed to allowing some to be created later. Is there a simple fix for this?


import kivy3
from kivy.app import App
from kivy3 import Scene, Renderer, PerspectiveCamera
from kivy3.loaders import OBJMTLLoader
from kivy.uix.floatlayout import FloatLayout
from kivy.clock import Clock
from kivy.graphics import Callback

class MainApp(App):

    def build(self):
        root = FloatLayout()
        self.renderer = Renderer()
        self.scene = Scene()
        self.camera = PerspectiveCamera(15, 1, 1, 1000)
        # load obj file
        loader = OBJMTLLoader()
        objPath = os.path.join(os.path.dirname(__file__), "./testnurbs.obj")
        obj = loader.load(objPath, "./testnurbs.mtl")
        self.toAdd = obj.children[1:]
        self.scene.add(obj.children[0])         #Box
        self.focal = obj.children[0]
        myFlag = 0
        if myFlag:
            self.scene.add(self.toAdd[0])       #Sphere
            self.toAdd.pop()
            self.renderer.render(self.scene, self.camera)
            self.scene.add(self.toAdd[0])       #Cylinder
            self.toAdd.pop()
            root.add_widget(self.renderer)
            self.scene.add(self.toAdd[0])       #Pyramid
            self.toAdd.pop()
        else:
            self.renderer.render(self.scene, self.camera)
            root.add_widget(self.renderer)
        self.renderer.bind(size=self.adjustAspect)
        Clock.schedule_interval(self.updateObj, 1. / 20)
        return root

    def updateObj(self, dt):
        for obj in self.scene.children:
            obj.pos.z -= 0.5
        if self.focal.pos.z <= -20 and self.toAdd:
            self.focal = self.toAdd.pop()
            self.scene.add(self.focal)
            self.renderer.render(self.scene, self.camera)

    def adjustAspect(self, inst, val):
        rsize = self.renderer.size
        aspect = rsize[0] / float(rsize[1])
        self.renderer.camera.aspect = aspect

m = MainApp()
m.run()```
mquasar commented 6 years ago

I'm facing the same problem. I need to add a new 3d object on_touch_up event and can't find a way to do it. Has anyone found a solution to this?

perceptualJonathan commented 6 years ago

I never did. The work around I came up with was creating a bunch of the objects that I knew I would need at some point, hide them out of sight, and then bring them into the frame when needed.

mquasar commented 6 years ago

I thought about doing something like that, it's not elegant, but if it works. Did you 'hide' the objects positioning them outside of the viewing area?

perceptualJonathan commented 6 years ago

Yes, I have a floor object, so I just put the other objects that I might need in the future under the floor.

jsuarez5341 commented 6 years ago

Late but needed, as this post is still high up on google. The reason this is happening is that render(scene, camera) converts the given scene into kivy instructions and adds them. You can add new objects by doing this manually: renderer._instructions.add(obj.as_instructions())