Viewport scene explorer + object cache integration
This commit is contained in:
parent
e277663ba0
commit
9c29567349
3 changed files with 199 additions and 9 deletions
|
|
@ -7,6 +7,8 @@
|
|||
#include "PyColor.h"
|
||||
#include "PyPositionHelper.h"
|
||||
#include "McRFPy_Doc.h"
|
||||
#include "PythonObjectCache.h"
|
||||
#include "McRFPy_API.h"
|
||||
#include <set>
|
||||
#include <cstring>
|
||||
|
||||
|
|
@ -925,15 +927,32 @@ int Viewport3D::init(PyViewport3DObject* self, PyObject* args, PyObject* kwds) {
|
|||
self->data->name = name;
|
||||
}
|
||||
|
||||
// Register in Python object cache for scene explorer repr
|
||||
if (self->data->serial_number == 0) {
|
||||
self->data->serial_number = PythonObjectCache::getInstance().assignSerial();
|
||||
PyObject* weakref = PyWeakref_NewRef((PyObject*)self, NULL);
|
||||
if (weakref) {
|
||||
PythonObjectCache::getInstance().registerObject(self->data->serial_number, weakref);
|
||||
Py_DECREF(weakref); // Cache owns the reference now
|
||||
}
|
||||
}
|
||||
|
||||
// Check if this is a Python subclass (for callback method support)
|
||||
PyObject* viewport3d_type = PyObject_GetAttrString(McRFPy_API::mcrf_module, "Viewport3D");
|
||||
if (viewport3d_type) {
|
||||
self->data->is_python_subclass = (PyObject*)Py_TYPE(self) != viewport3d_type;
|
||||
Py_DECREF(viewport3d_type);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#undef PyObjectType
|
||||
|
||||
} // namespace mcrf
|
||||
|
||||
// Methods array (outside namespace)
|
||||
// Methods array - outside namespace but PyObjectType still in scope via typedef
|
||||
typedef PyViewport3DObject PyObjectType;
|
||||
|
||||
PyMethodDef Viewport3D_methods[] = {
|
||||
// Add UIDRAWABLE_METHODS when ready
|
||||
UIDRAWABLE_METHODS,
|
||||
{NULL} // Sentinel
|
||||
};
|
||||
|
|
|
|||
|
|
@ -285,6 +285,10 @@ const char* ImGuiSceneExplorer::getTypeName(UIDrawable* drawable) {
|
|||
case PyObjectsEnum::UICAPTION: return "Caption";
|
||||
case PyObjectsEnum::UISPRITE: return "Sprite";
|
||||
case PyObjectsEnum::UIGRID: return "Grid";
|
||||
case PyObjectsEnum::UILINE: return "Line";
|
||||
case PyObjectsEnum::UICIRCLE: return "Circle";
|
||||
case PyObjectsEnum::UIARC: return "Arc";
|
||||
case PyObjectsEnum::UIVIEWPORT3D: return "Viewport3D";
|
||||
default: return "Unknown";
|
||||
}
|
||||
}
|
||||
|
|
|
|||
167
tests/demo/viewport3d_demo.py
Normal file
167
tests/demo/viewport3d_demo.py
Normal file
|
|
@ -0,0 +1,167 @@
|
|||
# viewport3d_demo.py - Visual demo of Viewport3D integration
|
||||
# Shows the 3D viewport as a UIDrawable alongside 2D elements
|
||||
|
||||
import mcrfpy
|
||||
import sys
|
||||
import math
|
||||
|
||||
# Create demo scene
|
||||
scene = mcrfpy.Scene("viewport3d_demo")
|
||||
|
||||
# Dark background frame
|
||||
bg = mcrfpy.Frame(pos=(0, 0), size=(1024, 768), fill_color=mcrfpy.Color(20, 20, 30))
|
||||
scene.children.append(bg)
|
||||
|
||||
# Title
|
||||
title = mcrfpy.Caption(text="Viewport3D Demo - PS1-Style 3D Rendering", pos=(20, 10))
|
||||
title.fill_color = mcrfpy.Color(255, 255, 255)
|
||||
scene.children.append(title)
|
||||
|
||||
# Create the 3D viewport - this is the star of the show!
|
||||
viewport = mcrfpy.Viewport3D(
|
||||
pos=(50, 60),
|
||||
size=(600, 450),
|
||||
render_resolution=(320, 240), # PS1 resolution for that retro look
|
||||
fov=60.0,
|
||||
camera_pos=(5.0, 3.0, 5.0),
|
||||
camera_target=(0.0, 0.0, 0.0),
|
||||
bg_color=mcrfpy.Color(25, 25, 50) # Dark blue background
|
||||
)
|
||||
scene.children.append(viewport)
|
||||
|
||||
# Info panel on the right
|
||||
info_panel = mcrfpy.Frame(pos=(670, 60), size=(330, 450),
|
||||
fill_color=mcrfpy.Color(30, 30, 40),
|
||||
outline_color=mcrfpy.Color(80, 80, 100),
|
||||
outline=2.0)
|
||||
scene.children.append(info_panel)
|
||||
|
||||
# Panel title
|
||||
panel_title = mcrfpy.Caption(text="Viewport Properties", pos=(690, 70))
|
||||
panel_title.fill_color = mcrfpy.Color(200, 200, 255)
|
||||
scene.children.append(panel_title)
|
||||
|
||||
# Property labels
|
||||
props = [
|
||||
("Position:", f"({viewport.x}, {viewport.y})"),
|
||||
("Size:", f"{viewport.w}x{viewport.h}"),
|
||||
("Render Res:", f"{viewport.render_resolution[0]}x{viewport.render_resolution[1]}"),
|
||||
("FOV:", f"{viewport.fov} degrees"),
|
||||
("Camera Pos:", f"({viewport.camera_pos[0]:.1f}, {viewport.camera_pos[1]:.1f}, {viewport.camera_pos[2]:.1f})"),
|
||||
("Camera Target:", f"({viewport.camera_target[0]:.1f}, {viewport.camera_target[1]:.1f}, {viewport.camera_target[2]:.1f})"),
|
||||
("", ""),
|
||||
("PS1 Effects:", ""),
|
||||
(" Vertex Snap:", "ON" if viewport.enable_vertex_snap else "OFF"),
|
||||
(" Affine Map:", "ON" if viewport.enable_affine else "OFF"),
|
||||
(" Dithering:", "ON" if viewport.enable_dither else "OFF"),
|
||||
(" Fog:", "ON" if viewport.enable_fog else "OFF"),
|
||||
(" Fog Range:", f"{viewport.fog_near} - {viewport.fog_far}"),
|
||||
]
|
||||
|
||||
y_offset = 100
|
||||
for label, value in props:
|
||||
if label:
|
||||
cap = mcrfpy.Caption(text=f"{label} {value}", pos=(690, y_offset))
|
||||
cap.fill_color = mcrfpy.Color(180, 180, 200)
|
||||
scene.children.append(cap)
|
||||
y_offset += 22
|
||||
|
||||
# Instructions at bottom
|
||||
instructions = mcrfpy.Caption(
|
||||
text="[1-4] Toggle PS1 effects | [WASD] Move camera | [Q/E] Camera height | [ESC] Quit",
|
||||
pos=(20, 530)
|
||||
)
|
||||
instructions.fill_color = mcrfpy.Color(150, 150, 150)
|
||||
scene.children.append(instructions)
|
||||
|
||||
# Status line
|
||||
status = mcrfpy.Caption(text="Status: Viewport3D rendering PS1-style 3D cube", pos=(20, 555))
|
||||
status.fill_color = mcrfpy.Color(100, 200, 100)
|
||||
scene.children.append(status)
|
||||
|
||||
# Animation state
|
||||
animation_time = [0.0]
|
||||
camera_orbit = [True]
|
||||
|
||||
# Camera orbit animation
|
||||
def update_camera(timer, runtime):
|
||||
animation_time[0] += runtime / 1000.0
|
||||
|
||||
if camera_orbit[0]:
|
||||
# Orbit camera around origin
|
||||
angle = animation_time[0] * 0.5 # Slow rotation
|
||||
radius = 7.0
|
||||
height = 4.0 + math.sin(animation_time[0] * 0.3) * 2.0
|
||||
|
||||
x = math.cos(angle) * radius
|
||||
z = math.sin(angle) * radius
|
||||
|
||||
viewport.camera_pos = (x, height, z)
|
||||
|
||||
# Key handler
|
||||
def on_key(key, state):
|
||||
if state != mcrfpy.InputState.PRESSED:
|
||||
return
|
||||
|
||||
key_name = str(key)
|
||||
|
||||
# Toggle PS1 effects with number keys
|
||||
if key == mcrfpy.Key.NUM_1:
|
||||
viewport.enable_vertex_snap = not viewport.enable_vertex_snap
|
||||
status.text = f"Vertex Snap: {'ON' if viewport.enable_vertex_snap else 'OFF'}"
|
||||
elif key == mcrfpy.Key.NUM_2:
|
||||
viewport.enable_affine = not viewport.enable_affine
|
||||
status.text = f"Affine Mapping: {'ON' if viewport.enable_affine else 'OFF'}"
|
||||
elif key == mcrfpy.Key.NUM_3:
|
||||
viewport.enable_dither = not viewport.enable_dither
|
||||
status.text = f"Dithering: {'ON' if viewport.enable_dither else 'OFF'}"
|
||||
elif key == mcrfpy.Key.NUM_4:
|
||||
viewport.enable_fog = not viewport.enable_fog
|
||||
status.text = f"Fog: {'ON' if viewport.enable_fog else 'OFF'}"
|
||||
|
||||
# Camera controls
|
||||
elif key == mcrfpy.Key.SPACE:
|
||||
camera_orbit[0] = not camera_orbit[0]
|
||||
status.text = f"Camera orbit: {'ON' if camera_orbit[0] else 'OFF (manual control)'}"
|
||||
|
||||
elif key == mcrfpy.Key.ESCAPE:
|
||||
mcrfpy.exit()
|
||||
|
||||
# Manual camera movement (when orbit is off)
|
||||
if not camera_orbit[0]:
|
||||
pos = list(viewport.camera_pos)
|
||||
speed = 0.5
|
||||
|
||||
if key == mcrfpy.Key.W:
|
||||
pos[2] -= speed
|
||||
elif key == mcrfpy.Key.S:
|
||||
pos[2] += speed
|
||||
elif key == mcrfpy.Key.A:
|
||||
pos[0] -= speed
|
||||
elif key == mcrfpy.Key.D:
|
||||
pos[0] += speed
|
||||
elif key == mcrfpy.Key.Q:
|
||||
pos[1] -= speed
|
||||
elif key == mcrfpy.Key.E:
|
||||
pos[1] += speed
|
||||
|
||||
viewport.camera_pos = tuple(pos)
|
||||
status.text = f"Camera: ({pos[0]:.1f}, {pos[1]:.1f}, {pos[2]:.1f})"
|
||||
|
||||
# Set up scene
|
||||
scene.on_key = on_key
|
||||
|
||||
# Create timer for camera animation
|
||||
timer = mcrfpy.Timer("camera_update", update_camera, 16) # ~60fps
|
||||
|
||||
# Activate scene
|
||||
mcrfpy.current_scene = scene
|
||||
|
||||
print("Viewport3D Demo loaded!")
|
||||
print("3D rendering enabled - spinning colored cube should be visible.")
|
||||
print()
|
||||
print("Controls:")
|
||||
print(" [1-4] Toggle PS1 effects")
|
||||
print(" [Space] Toggle camera orbit")
|
||||
print(" [WASD/QE] Manual camera control (when orbit off)")
|
||||
print(" [ESC] Quit")
|
||||
Loading…
Add table
Add a link
Reference in a new issue