Browse Source

Draw player pointers as colored squares with html

ssao
A.Olokhtonov 10 months ago
parent
commit
d8a5cd3fca
  1. 13
      README.md
  2. 18
      client/aux.js
  3. 30
      client/client_recv.js
  4. 14
      client/client_send.js
  5. 17
      client/default.css
  6. 1
      client/index.html
  7. 3
      client/index.js
  8. 7
      client/math.js
  9. 25
      client/webgl_draw.js
  10. 11
      client/webgl_listeners.js
  11. 6
      server/deserializer.js
  12. 6
      server/enums.js
  13. 18
      server/recv.js
  14. 28
      server/send.js
  15. 7
      server/serializer.js
  16. 4
      server/server.js

13
README.md

@ -6,6 +6,7 @@ Release: @@ -6,6 +6,7 @@ Release:
- Z-prepass fringe bug (also, when do we enable the prepass?)
- Textured quads (pictures, code already written in older version)
- Resize and move pictures (draw handles)
- Further investigate GC pauses in Firefox
- Debug
- Restore ability to limit event range
* Listeners/events/multiplayer
@ -13,22 +14,23 @@ Release: @@ -13,22 +14,23 @@ Release:
+ Fix blinking own stroke inbetween SYN->server and SYN->client
+ Drag with mouse button 3
+ Investigate skipped inputs on mobile (panning, zooming) [Events were not actually getting skipped. The stroke previews were just not being drawn]
- Save events to indexeddb (as some kind of a blob), restore on reconnect and page reload
- Do NOT use session id as player id LUL
- Save events to indexeddb (as some kind of a blob), restore on reconnect and page reload
- Local prediction for tools!
- Be able to have multiple "current" strokes per player. In case of bad internet this can happen!
- Missing features I do not consider bonus
* Missing features I do not consider bonus
+ Player pointers
- Player screens
- Eraser
- Line drawing
- Player screens/pointers
- Follow player (like Ligma)
- Color picker (or at the very least an Open Color color pallete)
- Undo/redo
- Dynamic svg cursor to represent the brush
- Polish
* Polish
* Use typedvector where appropriate
- Show what's happening while the desk is loading (downloading, processing, uploading to gpu)
- Settings panel (including the setting for "offline mode")
- Settings panel for config values (including the setting for "offline mode")
- Set up VAOs
- Presentation / "marketing"
- Title
@ -50,7 +52,6 @@ Bonus: @@ -50,7 +52,6 @@ Bonus:
- Further optimization
- Draw LOD size histogram for various cases (maybe we see that in our worst case 90% of strokes are down to 3-4 points)
- If we see lots of very low detail strokes, precompute zoom level for 3,4,... points left
- Further investigate GC pauses on Mobile Firefox
Bonus-bonus:
- Actually infinite canvas (replace floats with something, some kind of fixed point scheme? chunks? multilevel scheme?)

18
client/aux.js

@ -36,11 +36,13 @@ function event_size(event) { @@ -36,11 +36,13 @@ function event_size(event) {
let size = 4; // type
switch (event.type) {
case EVENT.PREDRAW: {
case EVENT.PREDRAW:
case EVENT.MOVE_CURSOR: {
size += 4 * 2;
break;
}
case EVENT.LEAVE:
case EVENT.CLEAR: {
break;
}
@ -153,4 +155,16 @@ function tv_clear(tv) { @@ -153,4 +155,16 @@ function tv_clear(tv) {
tv.size = 0;
}
function HTML(html) {
const template = document.createElement('template');
template.innerHTML = html.trim();
return template.content.firstChild;
}
function insert_player_cursor(player_id) {
const color = color_from_u32(player_id);
const cursor = HTML(`<div class="player-cursor" data-player-id="${player_id}"></div>`);
cursor.style.background = color;
document.querySelector('.html-hud').appendChild(cursor);
return cursor;
}

30
client/client_recv.js

@ -58,10 +58,17 @@ function des_event(d, state = null) { @@ -58,10 +58,17 @@ function des_event(d, state = null) {
break;
}
case EVENT.LEAVE:
case EVENT.CLEAR: {
break;
}
case EVENT.MOVE_CURSOR: {
event.x = des_f32(d);
event.y = des_f32(d);
break;
}
case EVENT.SET_COLOR: {
event.color = des_u32(d);
break;
@ -137,6 +144,8 @@ function init_player_defaults(state, player_id, color = config.default_color, wi @@ -137,6 +144,8 @@ function init_player_defaults(state, player_id, color = config.default_color, wi
'color': color,
'width': width,
'points': [],
'online': false,
'cursor': {'x': 0, 'y': 0},
};
}
@ -161,6 +170,27 @@ function handle_event(state, context, event, options = {}) { @@ -161,6 +170,27 @@ function handle_event(state, context, event, options = {}) {
break;
}
case EVENT.LEAVE: {
if (event.user_id in state.players) {
state.players[event.user_id].online = false;
draw_html(state);
}
break;
}
case EVENT.MOVE_CURSOR: {
if (event.user_id in state.players) {
state.players[event.user_id].cursor.x = event.x;
state.players[event.user_id].cursor.y = event.y;
state.players[event.user_id].online = true;
}
// Should we syncronize this to RAF?
draw_html(state);
break;
}
case EVENT.SET_COLOR: {
state.players[event.user_id].color = event.color;
break;

14
client/client_send.js

@ -79,6 +79,12 @@ function ser_event(s, event) { @@ -79,6 +79,12 @@ function ser_event(s, event) {
break;
}
case EVENT.MOVE_CURSOR: {
ser_f32(s, event.x);
ser_f32(s, event.y);
break;
}
case EVENT.SET_COLOR: {
ser_u32(s, event.color);
break;
@ -312,3 +318,11 @@ function clear_event(state) { @@ -312,3 +318,11 @@ function clear_event(state) {
'type': EVENT.CLEAR
};
}
function movecursor_event(x, y) {
return {
'type': EVENT.MOVE_CURSOR,
'x': x,
'y': y,
};
}

17
client/default.css

@ -51,6 +51,23 @@ canvas.mousemoving { @@ -51,6 +51,23 @@ canvas.mousemoving {
cursor: move;
}
.html-hud {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
pointer-events: none;
z-index: 1;
}
.html-hud .player-cursor {
position: absolute;
width: 16px;
height: 16px;
background: red;
}
.tools-wrapper {
position: fixed;
bottom: 0;

1
client/index.html

@ -27,6 +27,7 @@ @@ -27,6 +27,7 @@
<body>
<div class="main">
<canvas id="c"></canvas>
<div class="html-hud"></div>
<!-- <svg viewBox="0 0 600 600" xmlns="http://www.w3.org/2000/svg" style="position: absolute; left:0; top: 0; pointer-events: none;">
<polyline points="150,150 225,230 280,120" / fill="none" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="5">

3
client/index.js

@ -36,6 +36,9 @@ const EVENT = Object.freeze({ @@ -36,6 +36,9 @@ const EVENT = Object.freeze({
SET_COLOR: 11,
SET_WIDTH: 12,
CLEAR: 13, // clear predraw events from me (because I started a pan instead of drawing)
MOVE_CURSOR: 14,
MOVE_SCREEN: 15,
LEAVE: 16,
STROKE: 20,
RULER: 21, // gets re-written with EVENT.STROKE before sending to server

7
client/math.js

@ -9,6 +9,13 @@ function screen_to_canvas(state, p) { @@ -9,6 +9,13 @@ function screen_to_canvas(state, p) {
return {'x': xc, 'y': yc};
}
function canvas_to_screen(state, p) {
const xs = p.x * state.canvas.zoom + state.canvas.offset.x;
const ys = p.y * state.canvas.zoom + state.canvas.offset.y;
return {'x': xs, 'y': ys};
}
/*
function rdp_find_max(state, zoom, stroke, start, end) {
// Finds a point from the range [start, end) with the maximum distance from the line (start--end) that is also further than EPS

25
client/webgl_draw.js

@ -1,7 +1,7 @@ @@ -1,7 +1,7 @@
function schedule_draw(state, context) {
if (!state.timers.raf) {
window.requestAnimationFrame(() => {
draw(state, context)
draw(state, context);
});
state.timers.raf = true;
}
@ -45,6 +45,29 @@ function upload_square_rgba16ui_texture(gl, serializer, texture_size) { @@ -45,6 +45,29 @@ function upload_square_rgba16ui_texture(gl, serializer, texture_size) {
}
}
function draw_html(state) {
// HUD-like things. Player cursors, screens
for (const player_id in state.players) {
if (player_id === state.me) continue;
const player = state.players[player_id];
let player_cursor_element = document.querySelector(`.player-cursor[data-player-id="${player_id}"]`);
if (player_cursor_element === null && player.online) {
player_cursor_element = insert_player_cursor(player_id);
}
if (!player.online && player_cursor_element !== null) {
player_cursor_element.remove();
}
if (player_cursor_element && player.online) {
const screenp = canvas_to_screen(state, player.cursor);
player_cursor_element.style.transform = `translate(${Math.round(screenp.x)}px, ${Math.round(screenp.y)}px)`;
}
}
}
function draw(state, context) {
const cpu_before = performance.now();

11
client/webgl_listeners.js

@ -217,6 +217,14 @@ function mousemove(e, state, context) { @@ -217,6 +217,14 @@ function mousemove(e, state, context) {
let do_draw = false;
const screenp = {'x': window.devicePixelRatio * e.clientX, 'y': window.devicePixelRatio * e.clientY};
const canvasp = screen_to_canvas(state, screenp);
if (state.me in state.players && dist_v2(state.players[state.me].cursor, canvasp) > 5) {
state.players[state.me].cursor = canvasp;
fire_event(state, movecursor_event(canvasp.x, canvasp.y));
}
if (state.moving) {
state.canvas.offset.x += e.movementX;
state.canvas.offset.y += e.movementY;
@ -230,9 +238,6 @@ function mousemove(e, state, context) { @@ -230,9 +238,6 @@ function mousemove(e, state, context) {
do_draw = true;
}
const screenp = {'x': window.devicePixelRatio * e.clientX, 'y': window.devicePixelRatio * e.clientY};
const canvasp = screen_to_canvas(state, screenp);
if (state.drawing) {
geometry_add_point(state, context, state.me, canvasp);
fire_event(state, predraw_event(canvasp.x, canvasp.y));

6
server/deserializer.js

@ -56,6 +56,12 @@ export function event(d) { @@ -56,6 +56,12 @@ export function event(d) {
break;
}
case EVENT.MOVE_CURSOR: {
event.x = f32(d);
event.y = f32(d);
break;
}
case EVENT.CLEAR: {
break;
}

6
server/enums.js

@ -9,6 +9,10 @@ export const EVENT = Object.freeze({ @@ -9,6 +9,10 @@ export const EVENT = Object.freeze({
SET_COLOR: 11,
SET_WIDTH: 12,
CLEAR: 13,
MOVE_CURSOR: 14,
MOVE_SCREEN: 15,
LEAVE: 16,
STROKE: 20,
UNDO: 30,
REDO: 31,
@ -24,4 +28,4 @@ export const MESSAGE = Object.freeze({ @@ -24,4 +28,4 @@ export const MESSAGE = Object.freeze({
FULL: 103,
FIRE: 104,
JOIN: 105,
});
});

18
server/recv.js

@ -87,23 +87,7 @@ function recv_fire(d, session) { @@ -87,23 +87,7 @@ function recv_fire(d, session) {
}
}
for (const sid in sessions) {
const other = sessions[sid];
if (other.id === session.id) {
continue;
}
if (other.state !== SESSION.READY) {
continue;
}
if (other.desk_id != session.desk_id) {
continue;
}
send.send_fire(other.ws, event);
}
send.fire_event(session, event);
}
function handle_event(session, event) {

28
server/send.js

@ -10,11 +10,13 @@ function event_size(event) { @@ -10,11 +10,13 @@ function event_size(event) {
let size = 4 + 4; // type + user_id
switch (event.type) {
case EVENT.PREDRAW: {
case EVENT.PREDRAW:
case EVENT.MOVE_CURSOR: {
size += 4 * 2;
break;
}
case EVENT.LEAVE:
case EVENT.CLEAR: {
break;
}
@ -179,7 +181,7 @@ export function send_ack(ws, lsn) { @@ -179,7 +181,7 @@ export function send_ack(ws, lsn) {
ws.send(s.buffer);
}
export function send_fire(ws, event) {
function send_fire(ws, event) {
if (!ws) {
return;
}
@ -192,6 +194,26 @@ export function send_fire(ws, event) { @@ -192,6 +194,26 @@ export function send_fire(ws, event) {
ws.send(s.buffer);
}
export function fire_event(from_session, event) {
for (const sid in sessions) {
const other = sessions[sid];
if (other.id === from_session.id) {
continue;
}
if (other.state !== SESSION.READY) {
continue;
}
if (other.desk_id != from_session.desk_id) {
continue;
}
send_fire(other.ws, event);
}
}
async function sync_session(session_id) {
if (!(session_id in sessions)) {
return;

7
server/serializer.js

@ -54,6 +54,13 @@ export function event(s, event) { @@ -54,6 +54,13 @@ export function event(s, event) {
break;
}
case EVENT.MOVE_CURSOR: {
f32(s, event.x);
f32(s, event.y);
break;
}
case EVENT.LEAVE:
case EVENT.CLEAR: {
break;
}

4
server/server.js

@ -61,6 +61,8 @@ export function startup() { @@ -61,6 +61,8 @@ export function startup() {
close(ws, code, message) {
if (ws.data.session_id in sessions) {
const leave_event = {'type': EVENT.LEAVE, 'user_id': ws.data.session_id};
send.fire_event(sessions[ws.data.session_id], leave_event);
console.log(`session ${ws.data.session_id} closed`);
sessions[ws.data.session_id].state = SESSION.CLOSED;
sessions[ws.data.session_id].ws = null;
@ -74,4 +76,4 @@ export function startup() { @@ -74,4 +76,4 @@ export function startup() {
});
console.log(`Running on ${config.HOST}:${config.PORT}`)
}
}

Loading…
Cancel
Save