cogentcore / core

A free and open source framework for building powerful, fast, elegant 2D and 3D apps that run on macOS, Windows, Linux, iOS, Android, and the web with a single Go codebase, allowing you to Code Once, Run Everywhere.
http://cogentcore.org/core
BSD 3-Clause "New" or "Revised" License
1.73k stars 82 forks source link

XYZ: Mesh not updating visually when modified during runtime #1264

Closed diocarvalho closed 2 days ago

diocarvalho commented 6 days ago

Describe the bug

When modifying a mesh after the runWindow() function is called, the changes to the vertices are not reflected visually in the scene. The issue occurs when attempting to apply transformations like curving the vertices of a mesh in real-time. The mesh only updates visually if the transformation is applied before the window is created. Calling methods like scene.SetNeedsUpdate() or scene.SetNeedsRender() does not resolve the issue.

How to reproduce

How to reproduce:

Load a 3D mesh in the scene. Apply any vertex transformation (like curving the mesh) during the runtime, for example, when clicking a button. Observe that the mesh does not visually update in the scene, despite the changes being made to the vertex array. Expected behavior: The changes to the mesh's vertices should be visible immediately after the transformation during runtime.

Example code

var transitionPage *xyz.Solid

func main(){
    // Create window and setup scene
    b := core.NewBody("Viewer")
    se := xyzcore.NewScene(b)
    se.UpdateWidget()
    scene := se.SceneXYZ()
    scene.MultiSample = 1
    // Set bg
    se.Styler(func(s *styles.Style) {
        scene.Background = colors.Scheme.Select.Container
    })
    // Create Lights
    xyz.NewAmbient(scene, "ambient", 0.9, xyz.DirectSun)
    xyz.NewDirectional(scene, "directional", 1, xyz.DirectSun).Pos.Set(0, 2, 1)
    // Set default Camera
    scene.Camera.Pose.Pos.Set(0, 0, 10)
    scene.Camera.LookAt(math32.Vector3{}, math32.Vec3(0, 1, 0))
    // Create a *xyz.Solid from file
    CreatePagesFromObj("models/pages.obj", scene)
    // Need to apply curve on click
    core.NewButton(buttonsFrame).SetText("Apply Effect").OnClick(func(e events.Event) {
        // Here not work.
        CurveMesh(transitionPage.Mesh, 45, 1.5)
        // tryed updaters but no change
        transitionPage.UpdateMeshBBox()
        scene.SetNeedsUpdate()
        se.UpdateWidget().NeedsRender()
        scene.SetNeedsRender()   
    })

    // Curvature applied here works fine before runWindow()
    CurveMesh(transitionPage.Mesh, 45, 1.5)
    b.RunMainWindow()    
}
// Function to apply curvature to a mesh
func CurveMesh(mesh *xyz.GenMesh, curveFactor, zScale float32) {
    var zMax float32 = 0
    for i := 0; i < len(mesh.Vertex)-2; i += 3 {
        // Apply curve to the X-axis based on Z position
        mesh.Vertex[i] = math32.Sin(float32(mesh.Vertex[i+2])) * (math32.DegToRad(curveFactor))
        if mesh.Vertex[i+2] > zMax {
            zMax = mesh.Vertex[i+2]
        }
    }
    // Adjust Z position based on curvature and fixed scale
    reductFactor := curveFactor / 360 * zScale
    newZMax := zMax * (1 - reductFactor)
    for i := 0; i < len(mesh.Vertex)-2; i += 3 {
        zPerc := mesh.Vertex[i+2] / zMax
        mesh.Vertex[i+2] = newZMax * zPerc
    }
}
// Mesh loading and setting up
func CreatePagesFromObj(objFilePath string, scene *xyz.Scene) {
    pageGroup := xyz.NewGroup(scene)
    err := scene.OpenObj(objFilePath, pageGroup)
    if err != nil {
        fmt.Printf("Error importing model: %e", err)
    }
    transitionPageGroup := pageGroup.Children[0].(*xyz.Group)
    transitionPage = transitionPageGroup.Children[0].(*xyz.Solid)
}

Relevant output

No response

Platform

Windows

rcoreilly commented 2 days ago

Thanks for reporting this -- it is a bit unclear and we really need to add more docs on XYZ specifically. Here's a working example with some annotations for what's what.

package main

import (
    "fmt"

    "cogentcore.org/core/colors"
    "cogentcore.org/core/core"
    "cogentcore.org/core/events"
    "cogentcore.org/core/math32"
    "cogentcore.org/core/styles"
    "cogentcore.org/core/xyz"
    "cogentcore.org/core/xyz/examples/assets"
    _ "cogentcore.org/core/xyz/io/obj"
    "cogentcore.org/core/xyz/xyzcore"
)

var transitionPage *xyz.Solid

func main() {
    // Create window and setup scene
    b := core.NewBody("Viewer")
    se := xyzcore.NewScene(b)
    se.UpdateWidget()
    scene := se.SceneXYZ()
    scene.MultiSample = 1
    // Set bg
    se.Styler(func(s *styles.Style) {
        scene.Background = colors.Scheme.Select.Container
    })
    // Create Lights
    xyz.NewAmbient(scene, "ambient", 0.9, xyz.DirectSun)
    xyz.NewDirectional(scene, "directional", 1, xyz.DirectSun).Pos.Set(0, 2, 1)
    // Set default Camera
    scene.Camera.Pose.Pos.Set(0, 0, 10)
    scene.Camera.LookAt(math32.Vector3{}, math32.Vec3(0, 1, 0))
    // Create a *xyz.Solid from file
    CreatePagesFromObj("models/pages.obj", scene)
    // Need to apply curve on click
    core.NewButton(b).SetText("Apply Effect").OnClick(func(e events.Event) {
        CurveMesh(scene, transitionPage.Mesh.(*xyz.GenMesh), 45, 1.5)
        scene.SetNeedsUpdate() // <- updates the underlying XYZ scenegraph
        se.NeedsRender()       // <- updates the outer core widget to render to display
    })

    // Curvature applied here works fine before runWindow()
    // CurveMesh(transitionPage.Mesh.(*xyz.GenMesh), 45, 1.5)
    b.RunMainWindow()
}

// Function to apply curvature to a mesh
func CurveMesh(scene *xyz.Scene, mesh *xyz.GenMesh, curveFactor, zScale float32) {
    var zMax float32 = 0
    for i := 0; i < len(mesh.Vertex)-2; i += 3 {
        // Apply curve to the X-axis based on Z position
        mesh.Vertex[i] = math32.Sin(float32(mesh.Vertex[i+2])) * (math32.DegToRad(curveFactor))
        if mesh.Vertex[i+2] > zMax {
            zMax = mesh.Vertex[i+2]
        }
    }
    // Adjust Z position based on curvature and fixed scale
    reductFactor := curveFactor / 360 * zScale
    newZMax := zMax * (1 - reductFactor)
    for i := 0; i < len(mesh.Vertex)-2; i += 3 {
        zPerc := mesh.Vertex[i+2] / zMax
        mesh.Vertex[i+2] = newZMax * zPerc
    }
    scene.SetMesh(mesh) // <- drives updating of GPU version of the mesh
}

// Mesh loading and setting up
func CreatePagesFromObj(objFilePath string, scene *xyz.Scene) {
    pageGroup := xyz.NewGroup(scene)
    // err := scene.OpenObj(objFilePath, pageGroup)
    err := scene.OpenObjFS(assets.Content, "gopher.obj", pageGroup)
    if err != nil {
        fmt.Printf("Error importing model: %e", err)
    }
    transitionPageGroup := pageGroup.Children[0].(*xyz.Group)
    transitionPage = transitionPageGroup.Children[0].(*xyz.Solid)
}
rcoreilly commented 2 days ago

ps. we are probably being too fine-grained in the separate updating of xyz vs. widget -- are planning to revisit this logic soon.

rcoreilly commented 2 days ago

With #1271 you can just call se.UpdateWidget() in the OnClick to get the full update.

kkoreilly commented 2 days ago

I merged #1271, so @rcoreilly's code should work with the latest commit version of Cogent Core using only se.UpdateWidget() or se.Update() in the OnClick handler (in addition to CurveMesh, which needs to call SetMesh as the key updating call).