Make canvas response to key press event

We often create our custom iteractor style object to define our interactive behaviors.
Write some debug code in OnKeyPress event.
For web page, the element canvas is like Qt’s widget. Set property tabindex for responsing to user key press event.

<canvas id="canvas" style="position: absolute; left: 0; top: 0; width:100%; height:100%; " tabindex=-1></canvas>

Asynchronous call

It’s synchronous for JS code call wasm exported interface as introduced in post Synchronous call between JS and C function.
Asynchronous call can be implemented by js code directly.

        var resolveAfter2Seconds = function() {
            console.log("> starting slow promise");
            return new Promise(resolve => {
                setTimeout(function() {
                resolve("> slow");
                console.log("> slow promise is done");
                }, 2000);
            });
        };
        async function asyncCall() {
            console.log('> calling');
            const result = await resolveAfter2Seconds();
            console.log(result);
        }

        asyncCall();

        console.log( "> finish!" );

Output order:

Will it response to key press event immediately if we call a time-consuming interface before?

Conclusion:
The page will wait until the time-consuming interface finish, then responsing works.
Let’s create a 3D scene and a time-consuming interface to test our guess.

#include <iostream>
#include <stdio.h>
#include <vtkSmartPointer.h>
#include <vtkSphereSource.h>
#include <vtkActor.h>
#include <vtkConeSource.h>
#include <vtkRenderer.h>
#include <vtkRenderWindow.h>
#include <vtkPolyDataMapper.h>
#include <vtkRenderWindowInteractor.h>

#include "CustomIteractorStyle.h"

#ifdef __EMSCRIPTEN__
#include "vtkSDL2OpenGLRenderWindow.h"
#include "vtkSDL2RenderWindowInteractor.h"
#include <unistd.h>
#endif // __EMSCRIPTEN__

#define vtkSPtr vtkSmartPointer
#define vtkSPtrNew(Var, Type) vtkSPtr<Type> Var = vtkSPtr<Type>::New();

using namespace std;

// export the function.
extern "C"
{
    void setup()
    {
        printf( "let's start!\n" );
    }

    int compute( int a, int b )
    {
        int ans = 1;

        for( int i = 0; i < b; ++i )
        {
            sleep(1);
            ans = ans * a;
        }
        return ans;
    }
}

int main()
{
    vtkSPtrNew( cone, vtkConeSource );
    vtkSPtrNew( mapper, vtkPolyDataMapper );
    mapper->SetInputConnection( cone->GetOutputPort() );

    vtkSPtrNew( actor, vtkActor );
    actor->SetMapper( mapper );

    vtkSPtrNew( renderer, vtkRenderer );
    renderer->AddActor(actor);
    renderer->SetBackground( 0, 0, 0 );

#ifdef __EMSCRIPTEN__
    vtkSPtrNew(renderWindow, vtkSDL2OpenGLRenderWindow);
#else
    vtkSPtrNew(renderWindow, vtkRenderWindow);
#endif
    renderWindow->AddRenderer( renderer );

#ifdef __EMSCRIPTEN__
    vtkSPtrNew(renderWindowInteractor, vtkSDL2RenderWindowInteractor);
#else
    vtkSPtrNew(renderWindowInteractor, vtkRenderWindowInteractor);
#endif

    renderWindowInteractor->SetRenderWindow( renderWindow );
    renderer->ResetCamera();
        
    vtkSPtrNew( iStyle, CustomIteractorStyle );
    renderWindowInteractor->SetInteractorStyle( iStyle );

    renderWindow->Render();
    renderWindowInteractor->Start();
    return 0;
}

Configure CMakeLists.txt to export our test and main functions.

project(wasmTester)

if (VTK_VERSION VERSION_LESS "8.9") #"9.0.3"
    find_package( VTK REQUIRED )
else ()
    find_package( VTK COMPONENTS vtkCommonCore vtkRenderingCore
        vtkInteractionStyle vtkRenderingOpenGL2 vtkFiltersSources
        vtkRenderingFreeType IOGeometry )
endif()

include_directories(
    ${CMAKE_CURRENT_BINARY_DIR}
    ${CMAKE_CURRENT_SOURCE_DIR}
)

if(EMSCRIPTEN)
  set(emscripten_options)
  list(APPEND emscripten_options
    "--bind"
    "-O3"
    "-g"
    "-DLOG_OFF"
    "SHELL:-s USE_SDL=2"
    "SHELL:-s EXPORTED_FUNCTIONS=['_compute, _setup, _main']"
    "SHELL:-s EXPORTED_RUNTIME_METHODS=['allocate,stringToUTF8,UTF8ToString,FS,intArrayFromString']"
    "SHELL:-s EXTRA_EXPORTED_RUNTIME_METHODS=['ALLOC_NORMAL']"
    "SHELL:-s -fdebug-compilation-dir=."
    "SHELL:-s EXPORT_NAME=tester"
    "SHELL:-s ALLOW_MEMORY_GROWTH=1"
    "SHELL:-s DEMANGLE_SUPPORT=1"
    "SHELL:-s EMULATE_FUNCTION_POINTER_CASTS=0"
    "SHELL:-s ERROR_ON_UNDEFINED_SYMBOLS=0"
    "SHELL:-s MODULARIZE=1"
    "SHELL:-s WASM=1"
    "SHELL:-s --no-heap-copy"
    "SHELL:-s INITIAL_MEMORY=200MB"
    "SHELL:-s MAXIMUM_MEMORY=512MB"
    "SHELL:-s ASSERTIONS=2"
    "SHELL:-s TOTAL_MEMORY=512MB"
    "SHELL:-s DISABLE_EXCEPTION_CATCHING=0"
  )
    message("Configuring data directory for wasm..........")
endif()

set( cpps 
    main.cpp
    CustomIteractorStyle.cpp
    CustomIteractorStyle.h
    )

add_executable(${PROJECT_NAME} ${cpps})

if (VTK_VERSION VERSION_LESS "8.9")
  # old system
  include(${VTK_USE_FILE})
else ()
  # vtk_module_autoinit is needed
  message("autoinit module for higher version vtk")
  vtk_module_autoinit(
    TARGETS ${PROJECT_NAME}
    MODULES ${VTK_LIBRARIES}
    )
endif ()

target_compile_options(${PROJECT_NAME}
  PUBLIC
    ${emscripten_options}
)

target_link_options(${PROJECT_NAME}
  PUBLIC
    ${emscripten_options}
)

target_link_libraries( ${PROJECT_NAME} ${VTK_LIBRARIES} ${LINK_FLAGS} )

Core code for test key press event.

void CustomIteractorStyle::OnKeyPress()
{
	vtkRenderWindowInteractor* rwi = this->Interactor;
	int isCtrl = rwi->GetControlKey();
	std::string sym = rwi->GetKeySym();
	std::cout << "sym: " << sym << std::endl;
}

emscripten loops are not real loops

We will not get the following message output in browser. It seems like that program goes to an infinite loop. But we can still click on html button and use shortcut key to debug, so Emscripten’s event loop is not an infinite loop !

        var Module = {
            canvas: (function() {
                var canvas = document.getElementById('canvas');
                //...
            })(),
            onRuntimeInitialized:  function() {
                console.log('initialized');
                Module._main();
                console.log( "the message will not be showed!" );
            },
        };

Most graphical apps use infinite loops to sequentially render like the following code.

int main(int argc, char* argv[]) {
    while(1) {        
        renderFrame();
        SDL_Delay(time_to_next_frame());
    }
}

But Emscripten simulates an infinite loop, but in actuality just calls the loop function – in our case renderFrame() – at a specified number of frames per second.

int main(int argc, char* argv[]) {
    emscripten_set_main_loop(renderFrame, 0, 1);
}

Reference link: https://www.jamesfmackenzie.com/2019/12/03/webassembly-emscripten-loops/

All source files are uploaded to Github.
https://github.com/theArcticOcean/tutorials/tree/main/learnWebAssembly/jsCallCFunctionWith3D


0 0 votes
Article Rating
Subscribe
Notify of
guest

0 Comments
Inline Feedbacks
View all comments

XML To JSON
: Input your strings, the tool can convert XML to JSON for you.

X
0
Would love your thoughts, please comment.x
()
x