浏览代码

Check in webgldisplay renderer

rdb 10 年之前
父节点
当前提交
36db010ef3

+ 56 - 0
panda/src/webgldisplay/config_webgldisplay.cxx

@@ -0,0 +1,56 @@
+// Filename: config_webgldisplay.cxx
+// Created by:  rdb (01Apr15)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) Carnegie Mellon University.  All rights reserved.
+//
+// All use of this software is subject to the terms of the revised BSD
+// license.  You should have received a copy of this license along
+// with this source code in a file named "LICENSE."
+//
+////////////////////////////////////////////////////////////////////
+
+#include "config_webgldisplay.h"
+#include "webGLGraphicsPipe.h"
+#include "webGLGraphicsWindow.h"
+#include "webGLGraphicsStateGuardian.h"
+#include "graphicsPipeSelection.h"
+#include "dconfig.h"
+#include "pandaSystem.h"
+
+Configure(config_webgldisplay);
+NotifyCategoryDef(webgldisplay, "display");
+
+ConfigureFn(config_webgldisplay) {
+  init_libwebgldisplay();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: init_libwebgldisplay
+//  Description: Initializes the library.  This must be called at
+//               least once before any of the functions or classes in
+//               this library can be used.  Normally it will be
+//               called by the static initializers and need not be
+//               called explicitly, but special cases exist.
+////////////////////////////////////////////////////////////////////
+void
+init_libwebgldisplay() {
+  static bool initialized = false;
+  if (initialized) {
+    return;
+  }
+  initialized = true;
+
+  WebGLGraphicsPipe::init_type();
+  WebGLGraphicsWindow::init_type();
+  WebGLGraphicsStateGuardian::init_type();
+
+  GraphicsPipeSelection *selection = GraphicsPipeSelection::get_global_ptr();
+  selection->add_pipe_type(WebGLGraphicsPipe::get_class_type(),
+                           WebGLGraphicsPipe::pipe_constructor);
+
+  PandaSystem *ps = PandaSystem::get_global_ptr();
+  ps->set_system_tag("WebGL", "window_system", "HTML");
+}

+ 28 - 0
panda/src/webgldisplay/config_webgldisplay.h

@@ -0,0 +1,28 @@
+// Filename: config_webgldisplay.h
+// Created by:  rdb (01Apr15)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) Carnegie Mellon University.  All rights reserved.
+//
+// All use of this software is subject to the terms of the revised BSD
+// license.  You should have received a copy of this license along
+// with this source code in a file named "LICENSE."
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef CONFIG_WEBGLDISPLAY_H
+#define CONFIG_WEBGLDISPLAY_H
+
+#include "pandabase.h"
+#include "notifyCategoryProxy.h"
+#include "configVariableString.h"
+#include "configVariableBool.h"
+#include "configVariableInt.h"
+
+NotifyCategoryDecl(webgldisplay,,);
+
+extern void init_libwebgldisplay();
+
+#endif

+ 4 - 0
panda/src/webgldisplay/p3webgldisplay_composite1.cxx

@@ -0,0 +1,4 @@
+#include "config_webgldisplay.cxx"
+#include "webGLGraphicsPipe.cxx"
+#include "webGLGraphicsStateGuardian.cxx"
+#include "webGLGraphicsWindow.cxx"

+ 14 - 0
panda/src/webgldisplay/webGLGraphicsPipe.I

@@ -0,0 +1,14 @@
+// Filename: webGLGraphicsPipe.I
+// Created by:  rdb (01Apr15)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) Carnegie Mellon University.  All rights reserved.
+//
+// All use of this software is subject to the terms of the revised BSD
+// license.  You should have received a copy of this license along
+// with this source code in a file named "LICENSE."
+//
+////////////////////////////////////////////////////////////////////
+

+ 155 - 0
panda/src/webgldisplay/webGLGraphicsPipe.cxx

@@ -0,0 +1,155 @@
+// Filename: webGLGraphicsPipe.cxx
+// Created by:  rdb (01Apr15)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) Carnegie Mellon University.  All rights reserved.
+//
+// All use of this software is subject to the terms of the revised BSD
+// license.  You should have received a copy of this license along
+// with this source code in a file named "LICENSE."
+//
+////////////////////////////////////////////////////////////////////
+
+#include "webGLGraphicsPipe.h"
+#include "webGLGraphicsWindow.h"
+#include "webGLGraphicsStateGuardian.h"
+#include "config_webgldisplay.h"
+#include "frameBufferProperties.h"
+
+TypeHandle WebGLGraphicsPipe::_type_handle;
+
+////////////////////////////////////////////////////////////////////
+//     Function: WebGLGraphicsPipe::Constructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+WebGLGraphicsPipe::
+WebGLGraphicsPipe() {
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: WebGLGraphicsPipe::Destructor
+//       Access: Public, Virtual
+//  Description:
+////////////////////////////////////////////////////////////////////
+WebGLGraphicsPipe::
+~WebGLGraphicsPipe() {
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: WebGLGraphicsPipe::get_interface_name
+//       Access: Published, Virtual
+//  Description: Returns the name of the rendering interface
+//               associated with this GraphicsPipe.  This is used to
+//               present to the user to allow him/her to choose
+//               between several possible GraphicsPipes available on a
+//               particular platform, so the name should be meaningful
+//               and unique for a given platform.
+////////////////////////////////////////////////////////////////////
+string WebGLGraphicsPipe::
+get_interface_name() const {
+  return "WebGL";
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: WebGLGraphicsPipe::pipe_constructor
+//       Access: Public, Static
+//  Description: This function is passed to the GraphicsPipeSelection
+//               object to allow the user to make a default
+//               webGLGraphicsPipe.
+////////////////////////////////////////////////////////////////////
+PT(GraphicsPipe) WebGLGraphicsPipe::
+pipe_constructor() {
+  return new WebGLGraphicsPipe;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: WebGLGraphicsPipe::get_preferred_window_thread
+//       Access: Public, Virtual
+//  Description: Returns an indication of the thread in which this
+//               GraphicsPipe requires its window processing to be
+//               performed: typically either the app thread (e.g. X)
+//               or the draw thread (Windows).
+////////////////////////////////////////////////////////////////////
+GraphicsPipe::PreferredWindowThread WebGLGraphicsPipe::
+get_preferred_window_thread() const {
+  // JavaScript has no threads, so does it matter?
+  return PWT_draw;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: WebGLGraphicsPipe::make_output
+//       Access: Protected, Virtual
+//  Description: Creates a new window on the pipe, if possible.
+////////////////////////////////////////////////////////////////////
+PT(GraphicsOutput) WebGLGraphicsPipe::
+make_output(const string &name,
+            const FrameBufferProperties &fb_prop,
+            const WindowProperties &win_prop,
+            int flags,
+            GraphicsEngine *engine,
+            GraphicsStateGuardian *gsg,
+            GraphicsOutput *host,
+            int retry,
+            bool &precertify) {
+
+  if (!_is_valid) {
+    return NULL;
+  }
+
+  WebGLGraphicsStateGuardian *web_gl = 0;
+  if (gsg != NULL) {
+    DCAST_INTO_R(web_gl, gsg, NULL);
+  }
+
+  // First thing to try: a WebGLGraphicsWindow
+
+  if (retry == 0) {
+    if (((flags&BF_require_parasite)!=0)||
+        ((flags&BF_refuse_window)!=0)||
+        ((flags&BF_resizeable)!=0)||
+        ((flags&BF_size_track_host)!=0)||
+        ((flags&BF_rtt_cumulative)!=0)||
+        ((flags&BF_can_bind_color)!=0)||
+        ((flags&BF_can_bind_every)!=0)) {
+      return NULL;
+    }
+    return new WebGLGraphicsWindow(engine, this, name, fb_prop, win_prop,
+                                   flags, gsg, host);
+  }
+
+  // Second thing to try: a GLES2GraphicsBuffer
+  if (retry == 1) {
+    if ((host==0)||
+  //        (!gl_support_fbo)||
+        ((flags&BF_require_parasite)!=0)||
+        ((flags&BF_require_window)!=0)) {
+      return NULL;
+    }
+    // Early failure - if we are sure that this buffer WONT
+    // meet specs, we can bail out early.
+    if ((flags & BF_fb_props_optional)==0) {
+      if ((fb_prop.get_indexed_color() > 0)||
+          (fb_prop.get_back_buffers() > 0)||
+          (fb_prop.get_accum_bits() > 0)||
+          (fb_prop.get_multisamples() > 0)) {
+        return NULL;
+      }
+    }
+    // Early success - if we are sure that this buffer WILL
+    // meet specs, we can precertify it.
+    if ((web_gl != 0) &&
+        (web_gl->is_valid()) &&
+        (!web_gl->needs_reset()) &&
+        (fb_prop.is_basic())) {
+      precertify = true;
+    }
+    return new GLES2GraphicsBuffer(engine, this, name, fb_prop, win_prop,
+                                  flags, gsg, host);
+  }
+
+  // Nothing else left to try.
+  return NULL;
+}

+ 74 - 0
panda/src/webgldisplay/webGLGraphicsPipe.h

@@ -0,0 +1,74 @@
+// Filename: webGLGraphicsPipe.h
+// Created by:  rdb (01Apr15)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) Carnegie Mellon University.  All rights reserved.
+//
+// All use of this software is subject to the terms of the revised BSD
+// license.  You should have received a copy of this license along
+// with this source code in a file named "LICENSE."
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef WEBGLGRAPHICSPIPE_H
+#define WEBGLGRAPHICSPIPE_H
+
+#include "pandabase.h"
+#include "graphicsWindow.h"
+#include "graphicsPipe.h"
+
+class FrameBufferProperties;
+
+class WebGLGraphicsWindow;
+
+////////////////////////////////////////////////////////////////////
+//       Class : WebGLGraphicsPipe
+// Description : This graphics pipe represents the interface for
+//               creating OpenGL ES graphics windows on an X-based
+//               (e.g. Unix) client.
+////////////////////////////////////////////////////////////////////
+class WebGLGraphicsPipe : public GraphicsPipe {
+public:
+  WebGLGraphicsPipe();
+  virtual ~WebGLGraphicsPipe();
+
+  virtual string get_interface_name() const;
+  static PT(GraphicsPipe) pipe_constructor();
+
+public:
+  virtual PreferredWindowThread get_preferred_window_thread() const;
+
+protected:
+  virtual PT(GraphicsOutput) make_output(const string &name,
+                                         const FrameBufferProperties &fb_prop,
+                                         const WindowProperties &win_prop,
+                                         int flags,
+                                         GraphicsEngine *engine,
+                                         GraphicsStateGuardian *gsg,
+                                         GraphicsOutput *host,
+                                         int retry,
+                                         bool &precertify);
+
+public:
+  static TypeHandle get_class_type() {
+    return _type_handle;
+  }
+  static void init_type() {
+    GraphicsPipe::init_type();
+    register_type(_type_handle, "WebGLGraphicsPipe",
+                  GraphicsPipe::get_class_type());
+  }
+  virtual TypeHandle get_type() const {
+    return get_class_type();
+  }
+  virtual TypeHandle force_init_type() {init_type(); return get_class_type();}
+
+private:
+  static TypeHandle _type_handle;
+};
+
+#include "webGLGraphicsPipe.I"
+
+#endif

+ 14 - 0
panda/src/webgldisplay/webGLGraphicsStateGuardian.I

@@ -0,0 +1,14 @@
+// Filename: webGLGraphicsStateGuardian.I
+// Created by:  rdb (01Apr15)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) Carnegie Mellon University.  All rights reserved.
+//
+// All use of this software is subject to the terms of the revised BSD
+// license.  You should have received a copy of this license along
+// with this source code in a file named "LICENSE."
+//
+////////////////////////////////////////////////////////////////////
+

+ 177 - 0
panda/src/webgldisplay/webGLGraphicsStateGuardian.cxx

@@ -0,0 +1,177 @@
+// Filename: webGLGraphicsStateGuardian.cxx
+// Created by:  rdb (01Apr15)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) Carnegie Mellon University.  All rights reserved.
+//
+// All use of this software is subject to the terms of the revised BSD
+// license.  You should have received a copy of this license along
+// with this source code in a file named "LICENSE."
+//
+////////////////////////////////////////////////////////////////////
+
+#include "webGLGraphicsStateGuardian.h"
+#include "config_webgldisplay.h"
+
+extern "C" {
+  extern void* emscripten_GetProcAddress(const char *x);
+}
+
+TypeHandle WebGLGraphicsStateGuardian::_type_handle;
+
+////////////////////////////////////////////////////////////////////
+//     Function: WebGLGraphicsStateGuardian::Constructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+WebGLGraphicsStateGuardian::
+WebGLGraphicsStateGuardian(GraphicsEngine *engine, GraphicsPipe *pipe) :
+  GLES2GraphicsStateGuardian(engine, pipe)
+{
+  _context = 0;
+  _have_context = false;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: WebGLGraphicsStateGuardian::Destructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+WebGLGraphicsStateGuardian::
+~WebGLGraphicsStateGuardian() {
+  if (_context != 0) {
+    const char *target = NULL;
+    emscripten_set_webglcontextlost_callback(target, NULL, false, NULL);
+    emscripten_set_webglcontextrestored_callback(target, NULL, false, NULL);
+
+    if (emscripten_webgl_destroy_context(_context) != EMSCRIPTEN_RESULT_SUCCESS) {
+      webgldisplay_cat.error() << "Failed to destroy WebGL context!\n";
+    }
+    _context = 0;
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: WebGLGraphicsStateGuardian::choose_pixel_format
+//       Access: Private
+//  Description: Selects a visual or fbconfig for all the windows
+//               and buffers that use this gsg.  Also creates the GL
+//               context and obtains the visual.
+////////////////////////////////////////////////////////////////////
+void WebGLGraphicsStateGuardian::
+choose_pixel_format(const FrameBufferProperties &properties,
+                    const char *target) {
+
+  nassertv(_context == 0);
+
+  EmscriptenWebGLContextAttributes attribs;
+  emscripten_webgl_init_context_attributes(&attribs);
+
+  attribs.alpha = (properties.get_alpha_bits() > 0);
+  attribs.depth = (properties.get_depth_bits() > 0);
+  attribs.stencil = (properties.get_stencil_bits() > 0);
+  attribs.majorVersion = 1;
+  attribs.minorVersion = 0;
+  attribs.enableExtensionsByDefault = false;
+
+  EMSCRIPTEN_WEBGL_CONTEXT_HANDLE result;
+  result = emscripten_webgl_create_context(target, &attribs);
+
+  if (result > 0) {
+    _context = result;
+    _have_context = true;
+
+    // We may lose the WebGL context at any time, at which time we
+    // have to be prepared to drop all resources.
+    emscripten_set_webglcontextlost_callback(target, (void *)this, false, &on_context_event);
+    emscripten_set_webglcontextrestored_callback(target, (void *)this, false, &on_context_event);
+  } else {
+    webgldisplay_cat.error() << "Context creation failed.\n";
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: WebGLGraphicsStateGuardian::reset
+//       Access: Public, Virtual
+//  Description: Resets all internal state as if the gsg were newly
+//               created.
+////////////////////////////////////////////////////////////////////
+void WebGLGraphicsStateGuardian::
+reset() {
+  GLES2GraphicsStateGuardian::reset();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: WebGLGraphicsStateGuardian::on_context_event
+//       Access: Private, Static
+//  Description: WebGL may take the context away from us at any time.
+//               We have to be ready for that.
+////////////////////////////////////////////////////////////////////
+EM_BOOL WebGLGraphicsStateGuardian::
+on_context_event(int type, const void *, void *user_data) {
+  WebGLGraphicsStateGuardian *gsg = (WebGLGraphicsStateGuardian *)user_data;
+  nassertr(gsg != NULL, false);
+
+  if (type == EMSCRIPTEN_EVENT_WEBGLCONTEXTLOST) {
+    webgldisplay_cat.warning() << "WebGL context lost!\n";
+    gsg->release_all();
+    return true;
+
+  } else if (type == EMSCRIPTEN_EVENT_WEBGLCONTEXTRESTORED) {
+    webgldisplay_cat.warning() << "WebGL context restored!\n";
+    return true;
+  }
+
+  return false;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: WebGLGraphicsStateGuardian::get_extra_extensions
+//       Access: Protected, Virtual
+//  Description: This may be redefined by a derived class (e.g. glx or
+//               wgl) to get whatever further extensions strings may
+//               be appropriate to that interface, in addition to the
+//               GL extension strings return by glGetString().
+////////////////////////////////////////////////////////////////////
+void WebGLGraphicsStateGuardian::
+get_extra_extensions() {
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GLGraphicsStateGuardian::has_extension
+//       Access: Published, Virtual
+//  Description: Returns true if the indicated extension is reported
+//               by the GL system, false otherwise.  The extension
+//               name is case-sensitive.
+////////////////////////////////////////////////////////////////////
+bool WebGLGraphicsStateGuardian::
+has_extension(const string &extension) const {
+  nassertr(_context != 0, false);
+
+  // If the GSG asks for it, that is probably a good reason to
+  // activate the extension.
+  EM_BOOL result = emscripten_webgl_enable_extension(_context, extension.c_str());
+#ifndef NDEBUG
+  if (result) {
+    webgldisplay_cat.info() << "Activated extension: " << extension << "\n";
+  }
+#endif
+  return result;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: WebGLGraphicsStateGuardian::do_get_extension_func
+//       Access: Public, Virtual
+//  Description: Returns the pointer to the GL extension function with
+//               the indicated name.  It is the responsibility of the
+//               caller to ensure that the required extension is
+//               defined in the OpenGL runtime prior to calling this;
+//               it is an error to call this for a function that is
+//               not defined.
+////////////////////////////////////////////////////////////////////
+void *WebGLGraphicsStateGuardian::
+do_get_extension_func(const char *name) {
+  return emscripten_GetProcAddress(name);
+}

+ 72 - 0
panda/src/webgldisplay/webGLGraphicsStateGuardian.h

@@ -0,0 +1,72 @@
+// Filename: webGLGraphicsStateGuardian.h
+// Created by:  rdb (01Apr15)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) Carnegie Mellon University.  All rights reserved.
+//
+// All use of this software is subject to the terms of the revised BSD
+// license.  You should have received a copy of this license along
+// with this source code in a file named "LICENSE."
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef WEBGLGRAPHICSSTATEGUARDIAN_H
+#define WEBGLGRAPHICSSTATEGUARDIAN_H
+
+#include "pandabase.h"
+#include "webGLGraphicsPipe.h"
+#include "gles2gsg.h"
+
+#include <html5.h>
+
+////////////////////////////////////////////////////////////////////
+//       Class : WebGLGraphicsStateGuardian
+// Description : A specialization on GLES2GraphicsStateGuardian
+//               to add emscripten-specific context set-up.
+////////////////////////////////////////////////////////////////////
+class WebGLGraphicsStateGuardian : public GLES2GraphicsStateGuardian {
+public:
+  WebGLGraphicsStateGuardian(GraphicsEngine *engine, GraphicsPipe *pipe);
+  virtual ~WebGLGraphicsStateGuardian();
+
+  void choose_pixel_format(const FrameBufferProperties &properties,
+                           const char *target);
+
+  static EM_BOOL on_context_event(int type, const void *, void *user_data);
+
+  virtual void reset();
+
+private:
+  EMSCRIPTEN_WEBGL_CONTEXT_HANDLE _context;
+  bool _have_context;
+
+protected:
+  virtual void get_extra_extensions();
+  virtual bool has_extension(const string &extension) const;
+  virtual void *do_get_extension_func(const char *name);
+
+public:
+  static TypeHandle get_class_type() {
+    return _type_handle;
+  }
+  static void init_type() {
+    GLES2GraphicsStateGuardian::init_type();
+    register_type(_type_handle, "WebGLGraphicsStateGuardian",
+                  GLES2GraphicsStateGuardian::get_class_type());
+  }
+  virtual TypeHandle get_type() const {
+    return get_class_type();
+  }
+  virtual TypeHandle force_init_type() {init_type(); return get_class_type();}
+
+private:
+  static TypeHandle _type_handle;
+
+  friend class WebGLGraphicsWindow;
+};
+
+#include "webGLGraphicsStateGuardian.I"
+
+#endif

+ 14 - 0
panda/src/webgldisplay/webGLGraphicsWindow.I

@@ -0,0 +1,14 @@
+// Filename: webGLGraphicsWindow.I
+// Created by:  rdb (31Mar15)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) Carnegie Mellon University.  All rights reserved.
+//
+// All use of this software is subject to the terms of the revised BSD
+// license.  You should have received a copy of this license along
+// with this source code in a file named "LICENSE."
+//
+////////////////////////////////////////////////////////////////////
+

+ 480 - 0
panda/src/webgldisplay/webGLGraphicsWindow.cxx

@@ -0,0 +1,480 @@
+// Filename: webGLGraphicsWindow.cxx
+// Created by:  rdb (31Mar15)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) Carnegie Mellon University.  All rights reserved.
+//
+// All use of this software is subject to the terms of the revised BSD
+// license.  You should have received a copy of this license along
+// with this source code in a file named "LICENSE."
+//
+////////////////////////////////////////////////////////////////////
+
+#include "webGLGraphicsWindow.h"
+#include "webGLGraphicsStateGuardian.h"
+#include "config_webgldisplay.h"
+#include "mouseButton.h"
+#include "keyboardButton.h"
+
+#include <emscripten.h>
+
+TypeHandle WebGLGraphicsWindow::_type_handle;
+
+////////////////////////////////////////////////////////////////////
+//     Function: WebGLGraphicsWindow::Constructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+WebGLGraphicsWindow::
+WebGLGraphicsWindow(GraphicsEngine *engine, GraphicsPipe *pipe,
+                    const string &name,
+                    const FrameBufferProperties &fb_prop,
+                    const WindowProperties &win_prop,
+                    int flags,
+                    GraphicsStateGuardian *gsg,
+                    GraphicsOutput *host) :
+  GraphicsWindow(engine, pipe, name, fb_prop, win_prop, flags, gsg, host)
+{
+  GraphicsWindowInputDevice device =
+    GraphicsWindowInputDevice::pointer_and_keyboard(this, "keyboard_mouse");
+  add_input_device(device);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: WebGLGraphicsWindow::Destructor
+//       Access: Public, Virtual
+//  Description:
+////////////////////////////////////////////////////////////////////
+WebGLGraphicsWindow::
+~WebGLGraphicsWindow() {
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: WebGLGraphicsWindow::move_pointer
+//       Access: Published, Virtual
+//  Description: Forces the pointer to the indicated position within
+//               the window, if possible.
+//
+//               Returns true if successful, false on failure.  This
+//               may fail if the mouse is not currently within the
+//               window, or if the API doesn't support this operation.
+////////////////////////////////////////////////////////////////////
+bool WebGLGraphicsWindow::
+move_pointer(int device, int x, int y) {
+  return false;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: WebGLGraphicsWindow::begin_frame
+//       Access: Public, Virtual
+//  Description: This function will be called within the draw thread
+//               before beginning rendering for a given frame.  It
+//               should do whatever setup is required, and return true
+//               if the frame should be rendered, or false if it
+//               should be skipped.
+////////////////////////////////////////////////////////////////////
+bool WebGLGraphicsWindow::
+begin_frame(FrameMode mode, Thread *current_thread) {
+  PStatTimer timer(_make_current_pcollector, current_thread);
+
+  begin_frame_spam(mode);
+  if (_gsg == (GraphicsStateGuardian *)NULL) {
+    return false;
+  }
+
+  WebGLGraphicsStateGuardian *webgl_gsg;
+  DCAST_INTO_R(webgl_gsg, _gsg, false);
+
+  if (emscripten_webgl_make_context_current(webgl_gsg->_context) != EMSCRIPTEN_RESULT_SUCCESS) {
+    webgldisplay_cat.error()
+      << "Failed to make context current.\n";
+    return false;
+  }
+
+  if (mode == FM_render) {
+    // begin_render_texture();
+    clear_cube_map_selection();
+  }
+
+  _gsg->set_current_properties(&get_fb_properties());
+  return _gsg->begin_frame(current_thread);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: WebGLGraphicsWindow::end_frame
+//       Access: Public, Virtual
+//  Description: This function will be called within the draw thread
+//               after rendering is completed for a given frame.  It
+//               should do whatever finalization is required.
+////////////////////////////////////////////////////////////////////
+void WebGLGraphicsWindow::
+end_frame(FrameMode mode, Thread *current_thread) {
+  end_frame_spam(mode);
+  nassertv(_gsg != (GraphicsStateGuardian *)NULL);
+
+  if (mode == FM_render) {
+    // end_render_texture();
+    copy_to_textures();
+  }
+
+  _gsg->end_frame(current_thread);
+
+  if (mode == FM_render) {
+    trigger_flip();
+    clear_cube_map_selection();
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: WebGLGraphicsWindow::end_flip
+//       Access: Public, Virtual
+//  Description: This function will be called within the draw thread
+//               after begin_flip() has been called on all windows, to
+//               finish the exchange of the front and back buffers.
+//
+//               This should cause the window to wait for the flip, if
+//               necessary.
+////////////////////////////////////////////////////////////////////
+void WebGLGraphicsWindow::
+end_flip() {
+  GraphicsWindow::end_flip();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: WebGLGraphicsWindow::process_events
+//       Access: Public, Virtual
+//  Description: Do whatever processing is necessary to ensure that
+//               the window responds to user events.  Also, honor any
+//               requests recently made via request_properties()
+//
+//               This function is called only within the window
+//               thread.
+////////////////////////////////////////////////////////////////////
+void WebGLGraphicsWindow::
+process_events() {
+  GraphicsWindow::process_events();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: WebGLGraphicsWindow::set_properties_now
+//       Access: Public, Virtual
+//  Description: Applies the requested set of properties to the
+//               window, if possible, for instance to request a change
+//               in size or minimization status.
+//
+//               The window properties are applied immediately, rather
+//               than waiting until the next frame.  This implies that
+//               this method may *only* be called from within the
+//               window thread.
+//
+//               The return value is true if the properties are set,
+//               false if they are ignored.  This is mainly useful for
+//               derived classes to implement extensions to this
+//               function.
+////////////////////////////////////////////////////////////////////
+void WebGLGraphicsWindow::
+set_properties_now(WindowProperties &properties) {
+  GraphicsWindow::set_properties_now(properties);
+
+  if (properties.has_size()) {
+    emscripten_set_canvas_size(properties.get_x_size(), properties.get_y_size());
+    _properties.set_size(properties.get_size());
+    properties.clear_size();
+  }
+
+  if (properties.has_fullscreen() &&
+      properties.get_fullscreen() != _properties.get_fullscreen()) {
+
+    if (properties.get_fullscreen()) {
+      EMSCRIPTEN_RESULT result = emscripten_request_fullscreen(NULL, true);
+
+      if (result == EMSCRIPTEN_RESULT_SUCCESS) {
+        _properties.set_fullscreen(true);
+        properties.clear_fullscreen();
+
+      } else if (result == EMSCRIPTEN_RESULT_DEFERRED) {
+        // We can't switch to fullscreen just yet - this action is deferred
+        // until we're in an event handler.  We can't know for sure yet that
+        // fullscreen will be supported, but we shouldn't report failure.
+        properties.clear_fullscreen();
+      }
+    } else {
+      if (emscripten_exit_fullscreen() == EMSCRIPTEN_RESULT_SUCCESS) {
+        _properties.set_fullscreen(false);
+        properties.clear_fullscreen();
+      }
+    }
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: WebGLGraphicsWindow::close_window
+//       Access: Protected, Virtual
+//  Description: Closes the window right now.  Called from the window
+//               thread.
+////////////////////////////////////////////////////////////////////
+void WebGLGraphicsWindow::
+close_window() {
+  if (_gsg != (GraphicsStateGuardian *)NULL) {
+    emscripten_webgl_make_context_current(0);
+    _gsg.clear();
+  }
+
+  // Clear the assigned callbacks.
+  const char *target = NULL;
+  emscripten_set_fullscreenchange_callback(target, NULL, false, NULL);
+
+  emscripten_set_keypress_callback(target, NULL, false, NULL);
+  emscripten_set_keydown_callback(target, NULL, false, NULL);
+  emscripten_set_keyup_callback(target, NULL, false, NULL);
+
+  //emscripten_set_click_callback(target, NULL, false, NULL);
+  emscripten_set_mousedown_callback(target, NULL, false, NULL);
+  emscripten_set_mouseup_callback(target, NULL, false, NULL);
+  emscripten_set_mousemove_callback(target, NULL, false, NULL);
+  emscripten_set_mouseenter_callback(target, NULL, false, NULL);
+  emscripten_set_mouseleave_callback(target, NULL, false, NULL);
+
+  GraphicsWindow::close_window();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: WebGLGraphicsWindow::open_window
+//       Access: Protected, Virtual
+//  Description: Opens the window right now.  Called from the window
+//               thread.  Returns true if the window is successfully
+//               opened, or false if there was a problem.
+////////////////////////////////////////////////////////////////////
+bool WebGLGraphicsWindow::
+open_window() {
+  //WebGLGraphicsPipe *webgl_pipe;
+  //DCAST_INTO_R(webgl_pipe, _pipe, false);
+
+  const char *target = NULL;
+
+  // GSG Creation/Initialization
+  WebGLGraphicsStateGuardian *webgl_gsg;
+  if (_gsg == NULL) {
+    // There is no old gsg.  Create a new one.
+    webgl_gsg = new WebGLGraphicsStateGuardian(_engine, _pipe);
+    webgl_gsg->choose_pixel_format(_fb_properties, target);
+    _gsg = webgl_gsg;
+  } else {
+    // If the old gsg has the wrong pixel format, create a new one.
+    //DCAST_INTO_R(webgl_gsg, _gsg, false);
+    //if (!webgl_gsg->_fb_properties.subsumes(_fb_properties)) {
+    //  webgl_gsg = new WebGLGraphicsStateGuardian(_engine, _pipe);
+    //  webgl_gsg->choose_pixel_format(_fb_properties, target);
+    //  _gsg = webgl_gsg;
+    //}
+  }
+
+  if (_properties.has_size() && _properties.get_size() != LVecBase2i(1, 1)) {
+    emscripten_set_canvas_size(_properties.get_x_size(), _properties.get_y_size());
+  } else {
+    int width, height, fullscreen;
+    emscripten_get_canvas_size(&width, &height, &fullscreen);
+    _properties.set_size(width, height);
+    _properties.set_fullscreen(fullscreen > 0);
+  }
+
+  if (emscripten_webgl_make_context_current(webgl_gsg->_context) != EMSCRIPTEN_RESULT_SUCCESS) {
+    webgldisplay_cat.error()
+      << "Failed to make context current.\n";
+    return false;
+  }
+
+  webgl_gsg->reset();
+
+  _fb_properties.clear();
+  _fb_properties.set_rgb_color(true);
+  _fb_properties.set_force_hardware(true);
+  _fb_properties.set_back_buffers(1);
+
+  GLint red_bits, green_bits, blue_bits, alpha_bits, depth_bits, stencil_bits;
+
+  glGetIntegerv(GL_RED_BITS, &red_bits);
+  glGetIntegerv(GL_GREEN_BITS, &green_bits);
+  glGetIntegerv(GL_BLUE_BITS, &blue_bits);
+  glGetIntegerv(GL_ALPHA_BITS, &alpha_bits);
+  glGetIntegerv(GL_DEPTH_BITS, &depth_bits);
+  glGetIntegerv(GL_STENCIL_BITS, &stencil_bits);
+
+  _fb_properties.set_rgba_bits(red_bits, green_bits, blue_bits, alpha_bits);
+  _fb_properties.set_depth_bits(depth_bits);
+  _fb_properties.set_stencil_bits(stencil_bits);
+
+  // Set callbacks.
+  emscripten_set_fullscreenchange_callback(target, (void *)this, false, &on_fullscreen_event);
+
+  void *user_data = (void *)&_input_devices[0];
+
+  emscripten_set_keypress_callback(target, user_data, false, &on_keyboard_event);
+  emscripten_set_keydown_callback(target, user_data, false, &on_keyboard_event);
+  emscripten_set_keyup_callback(target, user_data, false, &on_keyboard_event);
+
+  //emscripten_set_click_callback(target, user_data, false, &on_mouse_event);
+  emscripten_set_mousedown_callback(target, user_data, false, &on_mouse_event);
+  emscripten_set_mouseup_callback(target, user_data, false, &on_mouse_event);
+  emscripten_set_mousemove_callback(target, user_data, false, &on_mouse_event);
+  emscripten_set_mouseenter_callback(target, user_data, false, &on_mouse_event);
+  emscripten_set_mouseleave_callback(target, user_data, false, &on_mouse_event);
+
+  return true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: WebGLGraphicsWindow::on_fullscreen_event
+//       Access: Private, Static
+//  Description:
+////////////////////////////////////////////////////////////////////
+EM_BOOL WebGLGraphicsWindow::
+on_fullscreen_event(int type, const EmscriptenFullscreenChangeEvent *event, void *user_data) {
+  WebGLGraphicsWindow *window = (WebGLGraphicsWindow *)user_data;
+  nassertr(window != NULL, false);
+
+  if (type == EMSCRIPTEN_EVENT_FULLSCREENCHANGE) {
+    WindowProperties props;
+    props.set_fullscreen(event->isFullscreen);
+    window->system_changed_properties(props);
+    return true;
+  }
+
+  return false;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: WebGLGraphicsWindow::on_keyboard_event
+//       Access: Private, Static
+//  Description:
+////////////////////////////////////////////////////////////////////
+EM_BOOL WebGLGraphicsWindow::
+on_keyboard_event(int type, const EmscriptenKeyboardEvent *event, void *user_data) {
+  GraphicsWindowInputDevice *device;
+  device = (GraphicsWindowInputDevice *)user_data;
+  nassertr(device != NULL, false);
+
+  if (type == EMSCRIPTEN_EVENT_KEYPRESS) {
+    // Chrome doesn't actually send this.  Weird.
+    return false;
+
+  } else if (type == EMSCRIPTEN_EVENT_KEYDOWN ||
+             type == EMSCRIPTEN_EVENT_KEYUP) {
+
+    ButtonHandle handle;
+    switch (event->which) {
+    case 16:
+      handle = KeyboardButton::shift();
+      break;
+
+    case 17:
+      handle = KeyboardButton::control();
+      break;
+
+    case 18:
+      handle = KeyboardButton::alt();
+      break;
+
+    case 20:
+      handle = KeyboardButton::caps_lock();
+      break;
+
+    case 186:
+      handle = KeyboardButton::ascii_key(';');
+      break;
+
+    case 188:
+      handle = KeyboardButton::ascii_key('.');
+      break;
+
+    case 189:
+      handle = KeyboardButton::ascii_key('-');
+      break;
+
+    case 190:
+      handle = KeyboardButton::ascii_key(',');
+      break;
+
+    case 191:
+      handle = KeyboardButton::ascii_key('?');
+      break;
+
+    case 192:
+      handle = KeyboardButton::ascii_key('`');
+      break;
+
+    case 220:
+      handle = KeyboardButton::ascii_key('\\');
+      break;
+
+    case 222:
+      handle = KeyboardButton::ascii_key('\'');
+      break;
+
+    default:
+      handle = KeyboardButton::ascii_key(tolower(event->which));
+    }
+
+    if (handle != ButtonHandle::none()) {
+      if (type == EMSCRIPTEN_EVENT_KEYUP) {
+        webgldisplay_cat.info() << "button up " << handle << "\n";
+        device->button_up(handle);
+      } else if (event->repeat) {
+        device->button_resume_down(handle);
+      } else {
+        webgldisplay_cat.info() << "button down " << handle << "\n";
+        device->button_down(handle);
+      }
+    } else {
+      webgldisplay_cat.info() << "button event code " << event->which << "\n";
+    }
+  }
+
+  return false;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: WebGLGraphicsWindow::on_mouse_event
+//       Access: Private, Static
+//  Description:
+////////////////////////////////////////////////////////////////////
+EM_BOOL WebGLGraphicsWindow::
+on_mouse_event(int type, const EmscriptenMouseEvent *event, void *user_data) {
+  GraphicsWindowInputDevice *device;
+  device = (GraphicsWindowInputDevice *)user_data;
+  nassertr(device != NULL, false);
+
+  double time = event->timestamp * 0.001;
+
+  switch (type) {
+  case EMSCRIPTEN_EVENT_MOUSEDOWN:
+    // Don't register out-of-bounds mouse downs.
+    if (event->canvasX < 0 || event->canvasY < 0) {
+      return false;
+    }
+    device->button_down(MouseButton::button(event->button), time);
+    return true;
+
+  case EMSCRIPTEN_EVENT_MOUSEUP:
+    device->button_up(MouseButton::button(event->button), time);
+    return true;
+
+  case EMSCRIPTEN_EVENT_MOUSEMOVE:
+    device->set_pointer_in_window(event->canvasX, event->canvasY, time);
+    return true;
+
+  case EMSCRIPTEN_EVENT_MOUSEENTER:
+    break;
+
+  case EMSCRIPTEN_EVENT_MOUSELEAVE:
+    device->set_pointer_out_of_window(time);
+    return true;
+
+  default:
+    break;
+  }
+
+  return false;
+}

+ 82 - 0
panda/src/webgldisplay/webGLGraphicsWindow.h

@@ -0,0 +1,82 @@
+// Filename: webGLGraphicsWindow.h
+// Created by:  rdb (31Mar15)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) Carnegie Mellon University.  All rights reserved.
+//
+// All use of this software is subject to the terms of the revised BSD
+// license.  You should have received a copy of this license along
+// with this source code in a file named "LICENSE."
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef WEBGLGRAPHICSWINDOW_H
+#define WEBGLGRAPHICSWINDOW_H
+
+#include "pandabase.h"
+
+#include "webGLGraphicsPipe.h"
+#include "graphicsWindow.h"
+
+#include <html5.h>
+
+////////////////////////////////////////////////////////////////////
+//       Class : WebGLGraphicsWindow
+// Description : An interface to Emscripten's WebGL interface that
+//               represents an HTML5 canvas.
+////////////////////////////////////////////////////////////////////
+class WebGLGraphicsWindow : public GraphicsWindow {
+public:
+  WebGLGraphicsWindow(GraphicsEngine *engine, GraphicsPipe *pipe,
+                      const string &name,
+                      const FrameBufferProperties &fb_prop,
+                      const WindowProperties &win_prop,
+                      int flags,
+                      GraphicsStateGuardian *gsg,
+                      GraphicsOutput *host);
+  virtual ~WebGLGraphicsWindow();
+
+  virtual bool move_pointer(int device, int x, int y);
+  virtual bool begin_frame(FrameMode mode, Thread *current_thread);
+  virtual void end_frame(FrameMode mode, Thread *current_thread);
+  virtual void end_flip();
+
+  virtual void process_events();
+  virtual void set_properties_now(WindowProperties &properties);
+
+protected:
+  virtual void close_window();
+  virtual bool open_window();
+
+private:
+  static EM_BOOL on_fullscreen_event(int type, const EmscriptenFullscreenChangeEvent *event, void *user_data);
+  static EM_BOOL on_keyboard_event(int type, const EmscriptenKeyboardEvent *event,
+                                   void *user_data);
+  static EM_BOOL on_mouse_event(int type, const EmscriptenMouseEvent *event,
+                                void *user_data);
+
+  string _canvas_id;
+
+public:
+  static TypeHandle get_class_type() {
+    return _type_handle;
+  }
+  static void init_type() {
+    GraphicsWindow::init_type();
+    register_type(_type_handle, "WebGLGraphicsWindow",
+                  GraphicsWindow::get_class_type());
+  }
+  virtual TypeHandle get_type() const {
+    return get_class_type();
+  }
+  virtual TypeHandle force_init_type() {init_type(); return get_class_type();}
+
+private:
+  static TypeHandle _type_handle;
+};
+
+#include "webGLGraphicsWindow.I"
+
+#endif